In some cases, an instance of a C++ class should not be copied at all. There are three ways to prevent such an object copy: keeping the copy constructor and assignment operator private, using a special non-copyable mixin, or deleting those special member functions.
A class that represents a wrapper stream of a file should not have its instance copied around. It will cause a confusion in the handling of the actual I/O system. In a similar spirit, if an instance holds a unique private object, copying the pointer does not make sense. A somehow related problem but not necessarily similar is the issue of object slicing.
The following illustration demonstrates a simple class Vehicle
that is supposed to have a unique owner, an instance of Person
.
class Car {
public:
Car(): owner() {}
void setOwner(Person *o) { owner = o; }
Person *getOwner() const { return owner; }
void info() const;
private:
Person *owner;
};
For this purpose, the implementation of Person
is as simple as:
struct Person {
std::string name;
};
To show the issue, a helper function info()
is implement 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;
}
From this example, it is obvious that an instance of Car
must not be copied. In particular, another clone of a similar car should not automatically belong to the same owner. In fact, running the subsequent code:
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 we prevent this accidental object copy?
Method 1: Private copy constructor and copy assignment operator
A very common technique is to declare both the copy constructor and copy assignment operator to be private. We do not even need to implement them. The idea is so that any attempt to perform a copy or an assignment will provoke a compile error.
In the above example, Car
will be modified to look like the following. Take a look closely at two additional private members of the class.
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 try again to assign an instance of Car
to a new one, the compiler will complain loudly:
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.
If writing two additional lines containing repetitive names is too cumbersome, a macro could be utilized instead. This is the approach used by WebKit, see its WTF_MAKE_NONCOPYABLE
macro from wtf/Noncopyable.h (do not be alarmed, in the context of WebKit source code, WTF here stands for Web Template Framework). Chromium code, as shown in the file base/macros.h, distinguishes between copy constructor and assignment, denoted as DISALLOW_COPY
and DISALLOW_ASSIGN
macros, respectively.
Method 2: Non-copyable mixin
The idea above can be extended to create a dedicated class which has the sole purpose to prevent object copying. It is often called as Noncopyable and typically used as a mixin. In our example, the Car
class can then be derived from this Noncopyable
.
Boost users may be already familiar with boost::noncopyable, the Boost flavor of the said mixin. A conceptual, self-contained implementation of that mixin will resemble something like the following:
class NonCopyable
{
protected:
NonCopyable() {}
~NonCopyable() {}
private:
NonCopyable(const NonCopyable &);
NonCopyable& operator=(const NonCopyable &);
};
Our lovely Car class can be written as:
class Car: private NonCopyable {
public:
Car(): owner() {}
void setOwner(Person *o) { owner = o; }
Person *getOwner() const { return owner; }
}
private:
Person *owner;
};
Compared to the first method, using Noncopyable
has the benefit of making the intention very clear. A quick glance at the class, right on its first line, and you know right away that its instance is not supposed to be copied.
Method 3: Deleted copy constructor and copy assignment operator
For modern applications, there is less and less reason to get stuck with the above workaround. Thanks to C++11, the solution becomes magically simple: just delete the copy constructor and assignment operator. Our class will look like this instead:
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;
};
Note that if you use boost::noncopyable
mixin with a compiler supporting C++11, the implementation of boost::noncopyable
also automatically deletes the said member functions.
With this approach, any accidental copy will result in a quite friendlier error message:
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; ^
So, which of the above three methods is your favorite?