Default behavior:
- By default, ctor (
T() ): calls base def. ctors and default members. - Copy ctor (
T(const T&) ): base calls are copied. ctors and members copy ctors. - Move ctor (
T(T&&) ): the base switches. ctors and members move ctors. - Assign (
T& operator=(const T&) ): the call assigns the base. and members appoint. - Transfer (
T& operator=(T&&) ): forwarding databases and transferring members. - Destructor (
~T() ): calls the member destructor and the database descriptor (reverse order).
For built-in types (int, etc.)
- By default, ctor: is set to 0 if explicitly specified
- Copy ctor: bitwise copy
- Move ctor: bitwise copy (no change to source)
- Assign: Bitmap
- Transmission: Bit Copy
- Destructor: does nothing.
Since pointers are also built-in types, this applies to int* (not what it points to).
Now, if you do not declare anything, your class T will simply contain an int * that does not own the specified int, so the copy of T just holds the pointer to the same int. This is the same as the behavior of C ++ 03. By default, movement is implemented for built-in types - copying. For classes, move in order (and depends on which members: just copies for built-in modules)
If you need to change this behavior, you must do it in a consistent manner: for example, if you want to “own” what you are pointing to, you need to
- initializing ctor to nullptr by default: this defines an "empty state", which we can reference later
- ctor creator initializing a given pointer
- copy of ctor initializing copy of pointer (this is a real change)
- dtor that removes sharp
- an appointment that removes the pointed ones and gets 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; }
Until we determine the purpose, but focus on the movement: If you do not declare it, since you announced the copy, it will be deleted: this makes the T object always copied, even if temporary (the same behavior as C ++ 03)
But if the source object is temporary, we can create an empty destination and change it:
T::T(T&& s) :T() { std::swap(_param, s._param); }
This is what is called movement.
Now assignment: before C ++ 11 T& operator=(const T& s) should check the self-assignment, make the destination empty and get a copy of the pointer:
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 passed parameter to generate a copy, thereby giving
T& operator=(T s)
Note that this also works in C ++ 98, but the pass-by copy will not be optimized on transition if s is temporary. This makes such an implementation disadvantageous in C ++ 98 and C ++ 03, but very convenient in C ++ 11.
Note that there is no need to specialize std::swap for T: std::swap(a,b); will work by performing three movements (without copying)
The practice of implementing the swap function is derived for the case when T has many members, being a mandatory replacement for both movement and destination. But it can be a regular private member function.