I had a conversation with a friend of mine about object assignment and construction the other day, and he made a point that assignment a = b for objects is (semantically) equivalent to destroying a and then re-constructing it from b (at the same place).
But of course, nobody (I think) writes assignment operators like this:
class A {
A& operator=(const A& rhs) {
this->~A();
this->A(rhs);
return *this;
}
A& operator=(A&& rhs) {
this->~A();
this->A(std::move(rhs));
return *this;
}
// etc.
};
[Notice: I have no clue how to manually call constructors/destructors on existing objects (I never had to do that!), so their invocations may make no formal sense, but I guess you can see the idea.]
What are the problems with this approach? I imagine there has to be a main show-stopper, but the bigger the list, the better.
There is a misused contruction, here:
class A {
A& operator=(const A& rhs) {
if(&a==this) return *this;
this->~A();
new(this) A(rhs);
return *this;
}
A& operator=(A&& rhs) {
if(&a==this) return *this;
this->~A();
new(this) A(std::move(rhs));
return *this;
}
// etc.
};
This is correct respect to the inplace ctor/dtor semantics, and thsi is what std::allocator does to destroy and construct elements in a buffer, so that must be correct, right?
Well... not properly: it all is about what A in fact contains and what the A ctor actually does.
If A just contains basic types and does not own resources that's fine, it works. It's just not idiomatic, but correct.
If A contains some other resources, that need to be acquired, managed and released well... you may be in trouble. And you also are if A is polymorphic (if ~A is virtual you destroy the entire object, but then you reconstruct just the A subobject).
The problem is that a constructor that acquires resources may fail, and an object that fails in construction and throws must not be destroyed since it has been never "constructed".
But if you are "assigning", you are not "creating", and if the in-place ctor fails, your object will exist (because it pre-exist in its own scope), but is in a state that cannot be managed by a further destruction: think to
{
A a,b;
a = b;
}
At the } b and a will be destroyed but if A(const A&) failed in a=b, and a throw is made in A::A, a is not existing, but will be improperly destroyed at the } that throw will immediately jump to.
A more idiomatic way is to have
class A
{
void swap(A& s) noexcept
{ /* exchanging resources between existing objects should never fail: you just swap pointers */ }
public:
A() noexcept { /* creates an object in a "null" recognizable state */ }
A(const A& s) { /* creates a copy: may fail! */ }
A(A&& s) noexcept { /*make it as null and... */ swap(s); } // if `s` is temporary will caryy old resource deletionon, and we keep it's own resource going
A& operator=(A s) noexcept { swap(s); return *this; }
~A() { /* handle resource deletion, if any */ }
};
Now,
a=b
will create a b copy as the s parameter in operator= (by means of A::A(const A&)).
If this fails, s will not exist and a and b are still valid (with their own old values), hence at scope exiting will be destroyed as normally.
If the copy succeed, the copyed resources and the actual a's will be exchanged, and when s dies at the } the old-a resources will be freed.
By converse
a = std::move(b)
Will make b as-temporary, the s parameter constructed via A(A&&), so b will swap with s (and becomes null) than s will swap with a. At the end, s will destroy old a resources, a will receive old b's and b will be in null state (so it can die peacefully when its scope ends)
The problem of "making A as null" must be implemented in both A() and A(A&&).
This may be by means of an helper member (an init, just like a swap) or by specifying member initializers, or by defining default initialization values for members (once for all)
First of all, calling the destructor manually is required only if the object was constructed using an overloaded operator new() with some expections like using the std::nothrow overloads.
And what you got to understand is the difference between copy construction and assignment operator: copy constructor is called when a new object is created from an existing object, as a copy of the existing object. And assignment operator is called when an already initialized object is assigned a new value from another existing object.
To sum up, example of assignment operator you've provided doesn't make sense - it got to have different semantics.
If you have further questions, leave a comment.
First it is not legal to call a copy constructor directly (at least in C++ compliant compilers.. VS2012 allows that) so the following isn't allowed:
// assignment operator
A& operator=(const A& rhs) {
this->~A();
this->A::A(rhs); <--- Invalid use
at that point you can either rely on compiler optimizations (see copy elision and RVO) or allocate it on the heap.
Many issues can arise if you try to do the above:
1) You might have exceptions thrown in the expression for the copy constructor
In this case you will have
// assignment operator
A& operator=(const A& rhs) {
cout << "copy assignment called" << endl;
this->~A();
A newObj(rhs); // Can throw and A is in invalid state!
return newObj;
}
To make it safe you should use the copy-and-swap idiom:
set& set::operator=(set const& source)
{
/* You actually don't need this. But if creating a copy is expensive then feel free */
if (&source == this)
return;
/*
* This line is invoking the copy constructor.
* You are copying 'source' into a temporary object not the current one.
* But the use of the swap() immediately after the copy makes it logically
* equivalent.
*/
set tmp(source);
this->swap(tmp);
return *this;
}
void swap(set& dst) throw ()
{
// swap member of this with members of dst
}
2) You might have problems with dynamically allocated memory
In case two instances of A shared a pointer, you might have a dangling pointer before being able to release it
a = a; // easiest case
...
// assignment operator
A& operator=(const A& rhs) {
this->~A(); <-- Freeing dynamically allocated memory
this->A::A(rhs); <--- Getting a pointer to nowhere
3) As Emilio noted, if the class is polymorphic you're not going to be able to re-instantiate that subclass (unless you trick it somehow with a CRTP-like technique)
4) Finally assignment and copy construction are two different operations. If A contains resources which are expensive to re-acquire, you might find yourself in a lot of troubles.
Related
This is an educational question, I am interested in what happens behind the scenes when I do:
SomeClass x(arg1, arg2, arg3); // An instance of SomeClass is constructed.
x = SomeClass(arg4, arg5, arg6); // Intent is to create a new instance.
SomeClass does not have operator= implemented.
Does the space allocated to x simply get overwritten as if it was newly allocated memory or what exactly happens? And when is it a good idea?
This can best be explained with the help of a small example:
Live on Coliru
struct A {
A(int a) { cout << "A::ctor\n"; } //ctor
A(const A& a) { cout << "A::copy\n"; } //copy ctor
A& operator=(const A& a) { cout << "A::operator=\n"; } //copy assign
};
int main()
{
A a(2); //calls constructor
a = A(10); //calls constructor first, then copy assignment
}
Output:
A::ctor
A::ctor
A::operator
The above is pretty self explanatory. For the first, only the constructor gets called. For the second, first the constructor is called and then copy assignment.
SomeClass does not have operator= implemented.
That doesn't matter because the compiler can generate one for you. If you explicitly delete it, then the above code will not compile. However, if you have a move constructor defined then that will be used:
(I highly recommend you read The rule of three/five/zero and understand it. It is among the top 5 things in C++ that you should know.)
A& operator=(const A& a) = delete; //copy assign deleted
A& operator=(A&& other) { cout << "move assigned\n"; } //move assign available
Now you maybe wondering what will happen if both copy and move assign are available. Lets see:
A a(2); //ctor
a = A(10); //ctor + move assign
A b(3); //ctor
b = a; // copy assign only
a = std::move(b); // move assign
For a = A(10) move assign is invoked because A(10) is an rvalue of the same type as what is on the left hand side of the =.
For the last case a = std::move(b);, we explicitly cast b to an rvalue (yes that's what std::move() does). Since it's an rvalue now, move assignment is invoked.
Does the space allocated to x simply get overwritten as if it was newly allocated memory or what exactly happens?
First the temporary is created: A(10). Space will of course be allocated for it.
It's result is then assigned to a, so previous values in a get overwritten
destructor for the temporary will be called
And when is it a good idea?
It is a good idea when you need it, it depends on your usecase. Generally I would recommend that don't copy assign unnecessarily.
Second line is call of constructor followed by call to assignment operator. Assigment default to shallow copy of non-static members into existing storage.
If you defined something that prevented compiler to create default operator=, i.e. you defined move constructor or move assignment, no assignment is possible unless you declared your own (why it is so surprising?) If default shallow copy is fine, you can write following declaration:
SomeClass& operator(const SomeClass&) = default;
= default provides mechanism to declare "default" behavior of special functions.
Now there is move assignment and in such case one would be preferred if it declared in given context. But it won't be declared by compiler if user provided destructor or copy\move constructor\assignment operator.
SomeClass& operator(SomeClass&&) = default;
Difference between two assignments exists only for class-types where "move" semantics may include transfer of ownership. For trivial types and primitive types it's a simple copy.
Compiler allowed to elide some actions including creation of storage for temporary object, so resulting code may actually write new values directly into x storage, provided that such elision won't change program behavior.
struct foo{
int* i;
foo(): i(new int(42)){}
foo(const foo&) = delete;
foo(foo&&) = default;
~foo(){
std::cout << "destructor: i" << std::endl;
delete(i);
}
};
int main()
{
foo f;
auto sp_f = std::make_shared<foo>(std::move(f));
}
This is bad because it seems that the destructor of f will be called once it moves into the shared_ptr. The shared_ptr will have a deleted pointer and will delete it after it goes out of scope, which means the pointer will be deleted two times.
How do I avoid this problem?
You need to define the move constructor to prevent deletion from the moved-from object:
foo(foo&& f): i(f.i) {
f.i = nullptr;
}
Now when to destructor of the old object is run, it won't delete the i, since deleting a null pointer is a no-op.
You should also define a move assignment operator and delete the copy assignment operator too.
The rule of three is really the rule of five now. If you have a class that can be moved from, you should define the move semantics yourself (plus the copy, destructor, etc).
As to how to do that, to quote cppreference's page on std::move, "... objects that have been moved from are placed in a valid but unspecified state." The unspecified state is typically what the object would look like if it had been default initialized, or what would happen if the objects had swap called on them.
A straightforward way is, as #zenith has answered, is to have the move constructor (or assignment operator) set the original pointer to nullptr. This way the data isn't freed, and the original object is still in a valid state.
Another common idiom is, as mentioned, to use swap. If a class needs its own copy and move semantics, a swap method would be handy as well. The move constructor would delegate initialization to the default constructor, and then call the swap method. In a move assignment operator, just call swap. The object being moved into will get the resources, and the other object's destructor will free the original ones.
It would usually look like this:
struct Foo
{
void* resource; //managed resource
Foo() : resource(nullptr) {} //default construct with NULL resource
Foo(Foo&& rhs) : Foo() //set to default value initially
{
this->swap(rhs); //now this has ownership, rhs has NULL
}
~Foo()
{
delete resource;
}
Foo& operator= (Foo&& rhs)
{
this->swap(rhs); //this has ownership, rhs has previous resource
}
void swap(Foo& rhs) //basic swap operation
{
std::swap(resource, rhs.resource); //thanks #M.M
}
};
Here is my following code. Once the constructor of A is complete, it immediately calls the ~B and deletes the allocated variable. I have the copy constructor and assignment constructor. Should I implement rule of five to prevent this?
EDIT: I have edited my copy and assignment constructor implementations, but still the destructor is being called.
class B
{
public:
C **table;
B()
{
table = new C *[TABLE_SIZE]();
}
B(const B& other)
{
table = new C *[TABLE_SIZE];
memcpy(table, other.table, sizeof(C *)* TABLE_SIZE);
}
B& operator = (const B& other)
{
if (this == &other)
{
return *this;
}
delete[] table;
table = new C *[TABLE_SIZE];
memcpy(table, other.table, sizeof(C *)* TABLE_SIZE);
return *this;
}
~B()
{
delete[] table;
}
}
class A
{
protected:
B funcA();
private:
B _b;
}
A::A()
{
this->_b = this->funcA();
// calls ~B here and destroys table
}
You do have the copy constructor and copy assignment operator implemented, but incorrectly. You implemented them to do exactly what the defaults do, which is of course wrong for your case of owning a dynamically allocated memory stored in a raw pointer. The Rule of Three of course means that you must implement them consistently.
In your case, this most likely means the copy operations must deep-copy the C object:
class B
{
public:
C *table;
B() : table(new C()) {}
B(const B& other) : table(new C(*other.table)) {}
B& operator = (const B& other)
{
if (this == &other)
{
return *this;
}
delete table;
table = new C(*other.table);
return *this;
}
~B()
{
delete table;
}
};
Notice that you default constructor wouldn't even compile - you were assigning a C* into a C**. I changed the code above to use a single allocation (and single-item delete) instead of an array. If you do have an array, the principle stays the same.
Of course, it's preferable to follow the Rule of Zero instead of the Rule of Three—use an appropriate smart pointer if possible. If your real use case is an array, that would be turning table into a std::vector<C>. Then you wouldn't have to supply the copy operations or destructor at all.
If you actually want to share one C among all copies of B, then you'll need to use an appropriate shared-ownership smart pointer instead, such as std::shared_ptr<C>.
Simply put, the temporary instance returned by funcA should be destroyed, that's normal and expected. The fact that your code is not functioning as a result means that your copy constructor and assignment operator are not functioning correctly.
But more fundamentally, you're violating several more important rules than the Rule of Three. You're failing to re-use the existing solution of std::vector. You're violating SRP by tying together memory management and whatever else B does when it's at home. You're failing to use RAII to manage your internal resources- a big exception safety fail.
Basically, the core problem is that you're even attempting to perform these operations. Don't. Just use std::vector<C*> and make the compiler and Standard library vendor implement them for you. They will save you a lot of code, and furthermore, they will be correct in ways that I'm going to guess that you don't know exist yet, like exception safety. The Rule of Three and the Rule of Five (which didn't even exist long enough to become a rule) are both vastly inferior to the Rule of Zero.
Here's B but implemented correctly:
class B {
public:
std::vector<C*> table;
B() : table(TABLE_SIZE) {}
};
Simple, ain't it?
As a side note, I don't know where you're getting your learning materials, but you should burn with fire any source so seriously outdated as to recommend self-assignment checks, and well, any of that code. May as well tell you that 640k of memory is all you'll ever need.
Your constructor is:
A::A()
{
this->_b = this->funcA();
// calls ~B here and destroys table
}
The comment is correct, although the table being destroyed is the table in the temporary object, not the table of _b. Since you fixed your copy-assignment operator this means that _b's table is fine.
The list of steps in executing this constructor is:
Call default constructor of _b
Call funcA, returning a temporary object of type B
Call _b 's assignment operator with that temporary object
Destruct the temporary object.
The constructor could be improved by supplying funcA() directly as initializer for _b, instead of first default-constructing _b then using its assignment operator. (But there is still a temporary object destruction).
There may also be another temporary object created and destroyed in the process of returning from the function, depending on your compiler settings. (usually there would not be).
When I learned C++ people told me to always implement at least rule of three methods.
Now I'm seeing the new "... = default;" from c++0x on stack overflow, and my question is:
Is there a c++11 standard implementation defined for those methods or is it compiler specific?
plus I would like to have some precisions:
What does the implementation looks like in term of code? (if it's generic)
Does this have an advantage compared to my example implementation below?
If you don't use assignment/copy constructor, what does *... = delete* do precisly, what's the difference with declaring them private? Answer (from #40two)
Is the new default= different from the old default implementation?
Disclaimer: when I'll need more advanced features in my methods, for sure I'll implements them myself. But I get used to implement assignment operator and copy constructor even when I never used them, just in order that the compiler don't.
What I used to do: (edited, #DDrmmr swap/move)
//File T.h
class T
{
public:
T(void);
T(const T &other);
T(const T &&other);
T &operator=(T other);
friend void swap(T &first, T &second);
~T(void);
protected:
int *_param;
};
//File T.cpp
T::T(void) :
_param(std::null)
{}
T::T(T &other)
: _param(other._param)
{}
T::T(T &&other)
: T()
{
swap(*this, other);
}
T &T::operator=(T other)
{
swap(*this, other);
return (*this);
}
friend void swap(T &first, T &second)
{
using std::swap;
swap(first._param, second._param);
}
T::~T(void)
{}
The default behavior is:
Default ctor ( T() ): calls bases def. ctors and members default ctors.
Copy ctor ( T(const T&) ): calls bases copy. ctors and members copy ctors.
Move ctor ( T(T&&) ): calls bases move. ctors and members move ctors.
Assign ( T& operator=(const T&) ): calls bases assign. and members assign.
Transfer ( T& operator=(T&&) ): calls bases transfer, and members transfer.
Destructor ( ~T() ): calls member destructor, and bases destructor (reverse order).
For built-in types (int etc.)
Default ctor: set to 0 if explicitly called
Copy ctor: bitwise copy
Move ctor: bitwise copy (no change on the source)
Assign: bitwise copy
Transfer: bitwise copy
Destructor: does nothing.
Since pointers are builtin types as well, this apply to int* ( not to what it points to).
Now, if you don't declare anything, your T class will just hold a int* that does not own the pointed int, so a copy of T will just hold a pointer to the same int. This is the same resulting behavior as C++03. Default implemented move for built-in types are copy. For classes are memberwise move (and depends on what members are: just copies for built-ins)
If you have to change this behavior, you have to do it coherently: for example, if you want to "own" what you point to, you need
a default ctor initializing to nullptr: this defines an "empty state" we can refer later
a creator ctor initializing to a given pointer
a copy ctor initializing to a copy of the pointed (this is the real change)
a dtor that deletes the pointed
an assign that deletes the pointed and receive a new copy of the pointed
.
T::T() :_param() {}
T::T(int* s) :_param(s) {}
T(const T& s) :_param(s._param? new int(*s._param): nullptr) {}
~T() { delete _param; } // will do nothing if _param is nullptr
Let's not define the assign, by now, but concentrate on the move:
If you don't declare it, since you declared the copy, it will be deleted: this makes a T object always being copied even if temporary (same behavior as c++03)
But if the source object is temporary, we can create an empty destination and swap them:
T::T(T&& s) :T() { std::swap(_param, s._param); }
This is what is called a move.
Now the assignment: before C++11 T& operator=(const T& s) should check against a self assignment, make the destination empty and receive a copy of the pointed:
T& operator=(const T& s)
{
if(this == &s) return *this; // we can shortcut
int* p = new int(s._param); //get the copy ...
delete _param; //.. and if succeeded (no exception while copying) ...
_param = p; // ... delete the old and keep the copy
return *this;
}
With C++11 we can use the parameter passing to generate the copy, thus giving
T& operator=(T s) //note the signature
{ std::swap(_param, s._param); return *this; }
Note that this works also in C++98, but the pass-by copy will not be optimized in pass-by move if s is temporary. This makes such implementation not profitable in C++98 and C++03 but really convenient in C++11.
Note also that there is no need to specialize std::swap for T: std::swap(a,b); will work, being implemented as three moves (not copy)
The practice to implement a swap function derives for the case where T has many members, being swap required in both move and assign. But it can be a regular private member function.
I want to redirect my copy operator to my copy constructor. Where in the latter I implement the proper logic for copying/contructing a new class based on the old avaialble class.
However, how is the proper way to do this? I "think" this one is maybe leaking memory, but I don't know how to do it without passing a pointer:
MyClass& MyClass::operator=(const MyClass& a) {
MyClass* b = new MyClass(a);
return *b;
}
Is this OK? If is not, what would be the proper way? Should I change the body of the method or the prototype?
Thank you.
No, an operator= should set the current object attributes to be the same as the object assigned. Your method assigns a new object on the heap, returns it as a reference (essentially leaking it) and leaves the object the operator was called on completely unchanged.
You should implement a method called, for example, CopyFrom(), which assigns all the object's attributes to match those of the passed in object (deep copying any heap allocated pointers whose lifetime is managed by MyClass) and then call THAT from both your copy constructor and your operator=.
class MyClass
{
public:
MyClass( const MyClass& in )
{
CopyFrom( in );
}
MyClass& operator=( const MyClass& in )
{
CopyFrom( in );
return *this;
}
private:
void CopyFrom( const MyClass& in )
{
... copies in's attributes into self.
}
};
As a rule, a copy assignment operator should never create a copy. Rather, it should copy data into the existing object that it's called on (the left-hand side of the assignment). For example:
class MyClass
{
public:
MyClass & operator = (const MyClass & RHS)
{
// Copy data from RHS into 'this'
m_value = RHS.m_value;
return *this;
}
private:
int m_value;
};
In this case, defining your own copy constructor isn't necessary because the default (compiler-provided) one would work fine. It's just an illustration though.
Unfortunately you can't invoke the copy constructor on the existing object. The copy-swap pattern is an alternative, but it can be less efficient.
Unless you're storing pointers inside of MyClass, the correct copy assignment operator is the default-generated one. If, however you need to implement one, you can write it in terms of your copy-constructor via the copy-swap idiom:
MyClass& MyClass::operator = (MyClass const& a) {
MyClass temp(a); // Call the copy constructor
using std::swap;
swap(temp, *this);
return *this;
}
The reason for the using std::swap is to enable argument-dependent lookup. If you define your own swap function for MyClass, it will be called. Else std::swap will be used as a fallback. (EDIT: You do in fact need to implement a custom swap in this case, or else you will get infinite recursion. std::swap will use the assignment operator, which will call std::swap, which will call the...)
The reason that this idiom is well-liked is because std::swap is a no-throw function. If your copy-constructor were to throw an exception, then your object you're assigning to is still in a valid state.
The "proper way" is to implement the assignment operator like an assignment operator: modify the contents of the object on which the operator is being called and return a reference to it.
Your current implementation will result in a memory leak, AND doesn't do any assignment (which is the main point of the assignment operator).
If you only want to write the assignment code once, and your class doesn't allocate memory in the constructor, you could do this:
MyClass::MyClass(const MyClass& a) {
*this = a;
}
MyClass& MyClass::operator=(const MyClass& a) {
if (&a == this)
return *this;
// Do assignment
return *this;
}
But I wouldn't recommend it.
Your code is totally wrong (sorry)! The assignment operator does
not assign anything, but allocates a pointer to a MyClass object,
creating a memory leak. My advice: avoid pointers or use some
smart pointer (shared_ptr, unique_ptr), but that is just a side note.
Maybe this is helpful:
#include <iostream>
#include <limits>
class X
{
public:
X(std::size_t n)
: m_size(n), m_data(new int[n])
{
std::cout << "Construct" << std::endl;
}
~X()
{
std::cout << "Destruct" << std::endl;
delete [] m_data;
}
// Exception safe assignment.
// Note: I am passing by value to enable copy elision and
// move semantics.
X& operator = (X x) {
std::cout << "Assign" << std::endl;
x.swap(*this);
return *this;
}
void swap(X& x) {
std::swap(m_size, x.m_size);
std::swap(m_data, x.m_data);
}
std::size_t size() const { return m_size; }
private:
std::size_t m_size;
int* m_data;
};
int main()
{
X x(1);
try {
x = X(2);
// To provoke an exception:
std::size_t n = std::numeric_limits<std::size_t>::max();
x = X(n);
}
catch(...) {
std::cout << "Exception" << std::endl;
}
std::cout << "Size: " << x.size() << std::endl;
return 0;
}
If you absolutely want to implement the assignment operator by copy constructor, use the following:
MyClass& MyClass::operator=(const MyClass& o)
{
this->~MyClass(); // destroy current object
new(this) MyClass(o); // use the copy constructor
return *this;
}
I cannot think of any situation in which this would be the best thing to do (other answers describe ways of implementation that are better in some situations).
Maybe (just trying to make things up here) if MyClass contains hundreds of int/float fields, and several dynamically-allocated pointers?
Duplicating them in constructor and assignment operator is too tedious and error-prone
Having a copying function that both constructor and assignment operator call - not ideal, because pointers have to be set to NULL first
The code above - will work with no additional effort!
However, having bare (non-smart) pointers in your class is discouraged. If you have such a class, then you have far worse problems than non-working assignment operator - you have to refactor first, and the problem will go away, together with all other bugs.