Reputation: 275
There are three way to use keyword 'new'. First is the normal way. Suppose Student is a class.
Student *pStu=new Student("Name",age);
Second way . Only ask for the memory space without calling the constructor.
Student *pArea=(Student*)operator new(sizeof(student));//
Third way is called 'placement new'. Only call the constructor to initialize the meomory space.
new (pArea)Student("Name",age);
So, I wrote some code below.
class Student
{
private:
std::string _name;
int _age;
public:
Student(std::string name, int age):_name(name), _age(age)
{
std::cout<<"in constructor!"<<std::endl;
}
~Student()
{
std::cout<<"in destructor!"<<std::endl;
}
Student & assign(const Student &stu)
{
if(this!=&stu)
{
//here! Is it a good way to implement the assignment?
this->~Student();
new (this)Student(stu._name,stu._age);
}
return *this;
}
};
This code is ok for gcc. But I'm not sure if it would cause errors or it was dangerous to call destructor explicitly. Call you give me some suggestions?
Upvotes: 2
Views: 184
Reputation: 153987
Your suggestion is a major anti-pattern: if the new
terminates
with an exception, you'll get undefined behavior, and if someone
tries to derive from your class, all sorts of weird things may
occur:
DerivedStudent a;
DerivedStudent b;
a = b; // Destructs a, and reconstructs it as a Student
and when a
goes out of scope, it is the destructor of
DerivedStudent
which will be called.
As a general rule, if you have to test for self-assignment, your assignment operator is not exception safe.
The goal of this idiom, of course, is to avoid code duplication between the copy constructor and the assignment operator, and to ensure that they have the same semantics. Generally, the best way to do this is to use the swap idiom, or something similar:
Student&
Student::operator=( Student const& other )
{
Student tmp( other );
swap( tmp );
return *this;
}
void
Student::swap( Student& other )
{
myName.swap( other.myName );
std::swap( myAge, other.myAge );
}
(One final, unrelated point. In practice, you're going to run into conflicts with names which begin with an underscore. In general, it's best to avoid leading or trailing underscore.)
Upvotes: 0
Reputation: 27385
But I'm not sure if it would cause errors or it was dangerous to call destructor explicitly. Call you give me some suggestions?
The code as you wrote it has three major drawbacks:
it is difficult to read
it is optimized for the uncommon case (always performs the self assignment check although you rarely perform self assignment in practice)
it is not exception safe
Consider using the copy and swap idiom:
Student & assign(Student stu) // pass stu by value, constructing temp instance
{ // this is the "copy" part of the idiom
using namespace std;
swap(*this, stu); // pass current values to temp instance to be destroyed
// and temp values to *this
return *this;
} // temp goes out of scope thereby destroying previous value of *this
This approach is exception-safe if swap doesn't throw and it has two potential drawbacks:
if Student is part of a class hierarchy (has virtual members) creating a temporary instance will be more costly.
in the case of self assignment the implementation should be a no-op (or a self equality test), but in that case it will create an extra object instance. The self-assignment case is very rare.
Upvotes: 0
Reputation: 477408
The problem with a "replacement-assignment" is that it's not exception safe. Consider this simplified, generic approach:
struct Foo
{
Foo & operator=(Foo const & rhs)
{
if (this == &rhs) { return *this; }
~Foo();
::new (this) Foo(rhs); // may throw!
}
// ...
};
Now if the copy constructor throws an exception, you're in trouble. You've already called your own destructor, so the inevitable next destructor call will cause undefined behaviour. You also cannot change the order of operations around, since you don't have any other memory.
I actually asked about this sort of "stepping on a landmine" behaviour in a question of mine.
Upvotes: 5