A C++ class instance should occasionally not be cloned at all. A non-copyable mixin, a private copy constructor and assignment operator, or the removal of those particular member functions are the three approaches to stop such object copies.
It is not appropriate to move an instance of a class that represents a file's wrapper stream. It will complicate how the actual I/O system is handled. In a similar vein, copying the pointer is useless if an instance has a special private object. The issue of object slicing is a somewhat comparable, but not necessarily same, topic.
The simple class Vehicle shown in the next picture is meant to have a singular owner, an instance of a Person.
class Car {
public:
Car(): owner() {}
void setOwner(Person *o) { owner = o; }
Person *getOwner() const { return owner; }
void info() const;
private:
Person *owner;
};
Implementing Person for this reason only requires that you do the following:
struct Person {
std::string name;
};
A helper method called info is implemented to illustrate the problem as follows:
void Car::info() const
{
if (owner) {
std::cout < < "Owner is " << owner->name < < std::endl;
} else {
std::cout << "This car has no owner." << std::endl;
}
This example makes it clear that a Car instance cannot be cloned. For example, the same owner should not automatically possess a second copy of an identical car. In fact, when the following code is executed:
Person joe;
joe.name = "Joe Sixpack";
Car sedan;
sedan.setOwner(&joe);
sedan.info();
Car anotherSedan = sedan;
anotherSedan.info();
will give the output:
Owner is Joe Sixpack
Owner is Joe Sixpack
How can this unintentional object copy be stopped?
Method 1: Private copy constructor and copy assignment operator
Declaring the copy assignment operator and copy constructor private is an often used tactic. Not even their implementation is necessary. The purpose is to ensure that any attempt to complete a copy or an assignment results in a compilation error.
Car will be altered to resemble the following in the example above. Examine two more class members who are in private closely.
class Car {
public:
Car(): owner() {}
void setOwner(Person *o) { owner = o; }
Person *getOwner() const { return owner; }
void info() const;
private:
Car(const Car&);
Car& operator=(const Car&);
Person *owner;
};
Now, if we attempt to assign an instance of Car to a new one once more, the compiler will scream loudly in protest:
example.cpp:35:22: error: calling a private constructor of class 'Car'
Car anotherSedan = sedan;
^
example.cpp:22:3: note: declared private here
Car(const Car&);
^
1 error generated.
A macro could be used in place of writing two extra lines containing the same names if that is too time-consuming. According to its WTF MAKE NONCOPYABLE macro in wtf/Noncopyable.h, WebKit takes this tack (do not be alarmed, in the context of WebKit source code, WTF here stands for Web Template Framework). The DISALLOW COPY and DISALLOW ASSIGN macros in the base/macros.h file, which is used in the code of the Chrome browser, distinguish between copy constructor and assignment.
Method 2: a mixin that is uncopiable
The aforementioned concept can be expanded to build a specific class whose primary function is to forbid object duplication. It is frequently referred to as Noncopyable and is frequently used as a mixin. The Car class in our illustration can thus be descended from this Noncopyable.
Users of Boost may already be familiar with the Boost version of the aforementioned mixin, boost::noncopyable. The following is an example of a conceptual, standalone implementation of the mixin:
class NonCopyable
{
protected:
NonCopyable() {}
~NonCopyable() {}
private:
NonCopyable(const NonCopyable &);
NonCopyable& operator=(const NonCopyable &);
};
Our wonderful Car class is spelled as follows:
Class Car: private NonCopyable {
public:
Car(): owner() {}
void setOwner(Person *o) { owner = o; }
Person *getOwner() const { return owner; }
}
private:
Person *owner;
};
Using Noncopyable has the advantage of making the goal very apparent in comparison to the first option. Just by taking a short look at the class's initial line, you can tell that its instance should not be cloned.
Method 3: Removed the copy assignment operator and copy constructor
The aforementioned workaround is becoming less and less necessary for newer apps. The answer is suddenly made easy by C++11: just get rid of the copy constructor and assignment operator. Instead, our class will appear as follows:
class Car {
public:
Car(const Car&) = delete;
void operator=(const Car&) = delete;
Car(): owner() {}
void setOwner(Person *o) { owner = o; }
Person *getOwner() const { return owner; }
private:
Person *owner;
};
It should be noted that the boost::noncopyable implementation also automatically removes the aforementioned member functions if you use the boost::noncopyable mixin with a compiler that supports C++11.
Any unintentional copy will produce a more friendlier error message with this method:
example.cpp:34:7: error: call to deleted constructor of 'Car'
Car anotherSedan = sedan;
^ ~~~~~
example.cpp:10:3: note: 'Car' has been explicitly marked deleted here
Car(const Car&) = delete;
^