C++ Class To Prevent Object Copies - C++ Programming Tutorial
C++ Course / Object-Oriented Programming / C++ Class To Prevent Object Copies

C++ Class To Prevent Object Copies

BLUF: Mastering C++ Class To Prevent Object Copies is a critical step in becoming a proficient C++ developer. This lesson provides a deep dive into the syntax, performance considerations, and real-world applications of this concept.
Key Performance Insight: C++ Class To Prevent Object Copies

C++ is renowned for its efficiency. Learn how C++ Class To Prevent Object Copies enables low-level control and high-performance computing in the tutorial below.

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.

Example

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:

Example

struct Person {
  std::string name;
};

A supporting function named info is created to demonstrate the issue in the following manner:

Example

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:

Example

Person joe;
  joe.name = "Joe Sixpack";
 

  Car sedan;
  sedan.setOwner(&joe);
  sedan.info();
  Car anotherSedan = sedan;
  anotherSedan.info();

will give the output:

Example

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.

Example

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

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:

Example

class NonCopyable
{
  protected:

    NonCopyable() {}
    ~NonCopyable() {}
  private: 
    NonCopyable(const NonCopyable &);
    NonCopyable& operator=(const NonCopyable &);
};

Our wonderful Car class is spelled as follows:

Example

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:

Example

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

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;
  ^

Input Required

This code uses input(). Please provide values below:

Logic Practice
Install Logic Practice
Add to home screen for a faster app-like experience