A C++ class instance may sometimes need to prevent cloning altogether. Three methods to achieve this include using a non-copyable mixin, defining a private copy constructor and assignment operator, or simply removing these specific member functions.
Moving an object of a class that acts as a wrapper for a file stream should be avoided as it can introduce complexities in managing the I/O system. Likewise, duplicating the pointer is ineffective when an instance contains a unique private object. While related, the concept of object slicing is not entirely identical but shares similarities with these scenarios.
The basic class Vehicle depicted in the following image is designed to have a single owner, represented by an object of type Person.
class Car {
public:
Car(): owner() {}
void setOwner(Person *o) { owner = o; }
Person *getOwner() const { return owner; }
void info() const;
private:
Person *owner;
};
Utilizing the Person class for this purpose solely necessitates that you perform the following steps:
struct Person {
std::string name;
};
A supporting function named info is created to demonstrate the issue in the following manner:
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 illustrates that it's not possible to clone a Car instance, meaning that the owner shouldn't automatically have a duplicate of the same car. When the code below is run:
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 accidental object duplication be prevented?
Method 1: Implementing a private copy constructor and copy assignment operator
Declaring the copy constructor and copy assignment operator as private is a commonly employed strategy. It is not required to provide their implementation. This approach serves to guarantee that any effort to perform a copy or assignment operation will lead to a compilation error.
The vehicle will undergo modifications to match the illustration provided earlier. Analyze two additional private class members carefully.
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;
};
If we try to assign a new instance of Car to another one again, the compiler will vehemently object with an error message:
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 employed instead of creating additional lines with identical names, which can be a tedious task. In the WebKit source code, the WTF MAKE NONCOPYABLE macro is utilized for this purpose (don't worry, in the WebKit context, WTF actually stands for Web Template Framework). The DISALLOW COPY and DISALLOW ASSIGN macros found in the base/macros.h file, utilized in the Chrome browser's codebase, differentiate between copy constructor and assignment operations.
Method 2: a mixin that is uncopiable
The previously mentioned idea can be extended to construct a distinct class with the main purpose of preventing object duplication. This class is commonly known as Noncopyable and is often employed as a mixin. Therefore, the Car class in our example can inherit from this Noncopyable class.
Boost users might have prior experience with the Boost variant of the mentioned mixin, boost::noncopyable. Here is an illustration of an abstract, independent 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;
};
Employing Noncopyable offers the benefit of clearly indicating the objective when contrasted with the initial choice. Simply by glancing at the first line of the class, it is evident that copying its instance is not intended.
Removed the copy assignment operator and copy constructor in Method 3.
The previously mentioned solution is gradually becoming less crucial for more recent applications. The solution is now simplified with C++11: simply eliminate the copy constructor and assignment operator. Subsequently, our class will be structured as shown below:
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 is important to mention that the boost::noncopyable implementation additionally eliminates the previously mentioned member functions when employing the boost::noncopyable mixin with a C++11 compatible compiler.
Any inadvertent duplication will result in a more user-friendly error notification using this approach:
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;
^