In C++11/14, an object can be transfered by move
or smark pointer.
(1) This is an example for move
:
class MoveClass {
private:
int *tab_;
int alloc_;
void Reset() {
tab_ = nullptr;
alloc_ = 0;
}
void Release() {
if (tab_) delete[] tab_;
tab_ = nullptr;
alloc_ = 0;
}
public:
MoveClass() : tab_(nullptr), alloc_(0) {}
~MoveClass() {
Release();
}
MoveClass(MoveClass && other) : tab_( other.tab_ ), alloc_( other.alloc_ ) {
other.Reset();
}
MoveClass & operator=(MoveClass && other) {
if (this == &other) return *this;
std::swap(tab_, other.tab_);
std::swap(alloc_, other.alloc_);
return *this;
}
void DoSomething() { /*...*/ }
};
When we use this movable MoveClass
, we can write code like this :
int main() {
MoveClass a;
a.DoSomething(); // now a has some memory resource
MoveClass b = std::move(a); // move a to b
return 0;
}
Always write move-constructor/move-operator= is boring, use shared_ptr/unique_ptr some times have the same effect, just like java, reference/pointer everywhere.
(2) Here is the example:
class NoMoveClass {
private:
int *tab_;
int alloc_;
void Release() {
if (tab_) delete[] tab_;
tab_ = nullptr;
alloc_ = 0;
}
public:
NoMoveClass() : tab_(nullptr), alloc_(0) {}
~NoMoveClass() {
Release();
}
MoveClass(MoveClass && other) = delete;
MoveClass & operator=(MoveClass && other) = delete;
void DoSomething() { /*...*/ }
};
We can use it like this:
int main() {
std::shared_ptr<NoMoveClass> a(new NoMoveClass());
a->DoSomething();
std::shared_ptr<NoMoveClass> b = a; // also move a to b by copy pointer.
return 0;
}
Is it a good habit to always use the 2nd one?
Why many libraries, STL use the 1st one, not the 1st one ?
Always write move-constructor/move-operator= is boring
You almost never need to write your own move constructor/assignment, because (as you mentioned) C++ supplies you with a number of basic resource managers - smart pointers, containers, smart locks etc.
By relying on those in your class you enable default move operations and that results in minimal code size as well as proper semantics:
class MoveClass {
private:
std::vector<int> data;
public:
void DoSomething() { /*...*/ }
};
Now you can use your class as in (1) or as a member in other classes, you can be sure that it has move semantics and you did it in the minimal possible amount of code.
The point is one usually only needs to implement move operations for the most low-level classes which are probably covered already by STL, or if some weird specific behavior is needed - both cases should be really rare and not result in "Always writing move-constructor/move-operator=".
Also notice that while approach (1) is unnecessarily verbose, (2) is just unacceptable - you have a resource managing class that doesn't do its job and as a result you have to wrap it in smart pointers everywhere in your code, making it harder to understand and eventually resulting in even more code than (1)