C++ Disable Copy And Assignments

In the C++programming language, a copy constructor is a special constructor for creating a new objectas a copy of an existing object. Copy constructors are the standard way of copying objects in C++, as opposed to cloning, and have C++-specific nuances.

The first argument of such a constructor is a reference to an object of the same type as is being constructed (const or non-const), which might be followed by parameters of any type (all having default values).

Normally the compiler automatically creates a copy constructor for each class (known as an implicit copy constructor) but for special cases the programmer creates the copy constructor, known as a user-defined copy constructor. In such cases, the compiler does not create one. Hence, there is always one copy constructor that is either defined by the user or by the system.

A user-defined copy constructor is generally needed when an object owns pointers or non-shareable references, such as to a file, in which case a destructor and an assignment operator should also be written (see Rule of three).

Definition[edit]

Copying of objects is achieved by the use of a copy constructor and an assignment operator. A copy constructor has as its first parameter a (possibly const or volatile) reference to its own class type. It can have more arguments, but the rest must have default values associated with them.[1] The following would be valid copy constructors for class :

X(constX&copy_from_me);X(X&copy_from_me);X(volatileX&copy_from_me);X(constvolatileX&copy_from_me);X(X&copy_from_me,int=0);X(constX&copy_from_me,double=1.0,int=42);...

The first one should be used unless there is a good reason to use one of the others. One of the differences between the first and the second is that temporaries can be copied with the first. For example:

Xa=X();// valid given X(const X& copy_from_me) but not valid given X(X& copy_from_me)// because the second wants a non-const X&// to create a, the compiler first creates a temporary by invoking the default constructor// of X, then uses the copy constructor to initialize as a copy of that temporary. // Temporary objects created while program execution are always of const type. So, const keyword is required.// For some compilers both versions actually work but this behaviour should not be relied // upon because it's non-standard.

Another difference between them is the obvious:

constXa;Xb=a;// valid given X(const X& copy_from_me) but not valid given X(X& copy_from_me)// because the second wants a non-const X&

The form of the copy constructor is used when it is necessary to modify the copied object. This is very rare but it can be seen used in the standard library's . A reference must be provided:

Xa;Xb=a;// valid if any of the copy constructors are defined// since a reference is being passed.

The following are invalid copy constructors (Reason - is not passed as reference) :

X(Xcopy_from_me);X(constXcopy_from_me);

because the call to those constructors would require a copy as well, which would result in an infinitely recursive call.

The following cases may result in a call to a copy constructor:

  1. When an object is returned by value
  2. When an object is passed (to a function) by value as an argument
  3. When an object is thrown
  4. When an object is caught
  5. When an object is placed in a brace-enclosed initializer list

These cases are collectively called copy-initialization and are equivalent to:[2]

It is however, not guaranteed that a copy constructor will be called in these cases, because the C++ Standard allows the compiler to optimize the copy away in certain cases, one example being the return value optimization (sometimes referred to as RVO).

Operation[edit]

An object can be assigned value using one of the two techniques:

  • Explicit assignment in an expression
  • Initialization

Explicit assignment in an expression[edit]

Objecta;Objectb;a=b;// translates as Object::operator=(const Object&), thus a.operator=(b) is called // (invoke simple copy, not copy constructor!)

Initialization[edit]

An object can be initialized by any one of the following ways.

a. Through declaration

Objectb=a;// translates as Object::Object(const Object&) (invoke copy constructor)

b. Through function arguments

c. Through function return value

The copy constructor is used only for initializations, and does not apply to assignments where the assignment operator is used instead.

The implicit copy constructor of a class calls base copy constructors and copies its members by means appropriate to their type. If it is a class type, the copy constructor is called. If it is a scalar type, the built-in assignment operator is used. Finally, if it is an array, each element is copied in the manner appropriate to its type.[3]

By using a user-defined copy constructor the programmer can define the behavior to be performed when an object is copied.

Examples[edit]

These examples illustrate how copy constructors work and why they are required sometimes.

Implicit copy constructor[edit]

Let us consider the following example:

#include<iostream.h>classPerson{public:intage;explicitPerson(inta):age(a){}};intmain(){Persontimmy(10);Personsally(15);Persontimmy_clone=timmy;std::cout<<timmy.age<<" "<<sally.age<<" "<<timmy_clone.age<<std::endl;timmy.age=23;std::cout<<timmy.age<<" "<<sally.age<<" "<<timmy_clone.age<<std::endl;}

Output

10 15 10 23 15 10

As expected, timmy has been copied to the new object, timmy_clone. While timmy's age was changed, timmy_clone's age remained the same. This is because they are totally different objects.

The compiler has generated a copy constructor for us, and it could be written like this:

Person(constPerson&other):age(other.age)// calls the copy constructor of the age{}

So, when do we really need a user-defined copy constructor? The next section will explore that question.

User-defined copy constructor[edit]

Now, consider a very simple dynamic array class like the following:

#include<iostream>classArray{public:intsize;int*data;explicitArray(intsz):size(sz),data(newint[size]){}~Array(){if(data!=NULL)delete[]this->data;}};intmain(){Arrayfirst(20);first.data[0]=25;{Arraycopy=first;std::cout<<first.data[0]<<" "<<copy.data[0]<<std::endl;}// (1)first.data[0]=10;// (2)}

Output

25 25 Segmentation fault

Since we did not specify a copy constructor, the compiler generated one for us. The generated constructor would look something like:

Array(constArray&other):size(other.size),data(other.data){}

The problem with this constructor is that it performs a shallow copy of the data pointer. It only copies the address of the original data member; this means they both share a pointer to the same chunk of memory, which is not what we want. When the program reaches line (1), copy's destructor gets called (because objects on the stack are destroyed automatically when their scope ends). Array's destructor deletes the data array of the original, therefore when it deleted copy's data, because they share the same pointer, it also deleted first's data. Line (2) now accesses invalid data and writes to it! This produces the infamous segmentation fault.

If we write our own copy constructor that performs a deep copy then this problem goes away.

// for std::copy#include<algorithm>Array(constArray&other):size(other.size),data(newint[other.size]){std::copy(other.data,other.data+other.size,data);}

Here, we are creating a new int array and copying the contents to it. Now, other's destructor deletes only its data, and not first's data. Line (2) will not produce a segmentation fault anymore.

Instead of doing a deep copy right away, there are some optimization strategies that can be used. These allow you to safely share the same data between several objects, thus saving space. The copy-on-write strategy makes a copy of the data only when it is written to. Reference counting keeps the count of how many objects are referencing the data, and will delete it only when this count reaches zero (e.g. boost::shared_ptr).

Copy constructors and templates[edit]

Contrary to expectations, a template copy constructor is not a user-defined copy constructor. Thus it is not enough to just have:

template<typenameA>Array::Array(Aconst&other):size(other.size()),data(newint[other.size()]){std::copy(other.begin(),other.end(),data);}

(Note that the type of can be .) A user-defined, non-template copy constructor must also be provided for construction of Array from Array.

Bitwise copy constructor[edit]

There is no such thing as "bitwise copy constructor" in C++. However, the default generated copy constructor copies by invoking copy constructors on members, and for a raw pointer member this will copy the raw pointer (i.e. not a deep copy).

Logical copy constructor[edit]

A logical copy constructor makes a true copy of the structure as well as its dynamic structures. Logical copy constructors come into the picture mainly when there are pointers or complex objects within the object being copied.

Explicit copy constructor[edit]

An explicit copy constructor is one that is declared explicit by using the explicit keyword. For example:

explicitX(constX&copy_from_me);

It is used to prevent copying of objects at function calls or with the copy-initialization syntax.

See also[edit]

References[edit]

External links[edit]

It can be seen that in a logical copy constructor, a new dynamic member variable is created for the pointer along with copying the values. [4]

The latest version of this topic can be found at Explicitly Defaulted and Deleted Functions.

In C++11, defaulted and deleted functions give you explicit control over whether the special member functions are automatically generated. Deleted functions also give you simple language to prevent problematic type promotions from occurring in arguments to functions of all types—special member functions, as well as normal member functions and non-member functions—which would otherwise cause an unwanted function call.

In C++, the compiler automatically generates the default constructor, copy constructor, copy-assignment operator, and destructor for a type if it does not declare its own. These functions are known as the special member functions, and they are what make simple user-defined types in C++ behave like structures do in C. That is, you can create, copy, and destroy them without any additional coding effort. C++11 brings move semantics to the language and adds the move constructor and move-assignment operator to the list of special member functions that the compiler can automatically generate.

This is convenient for simple types, but complex types often define one or more of the special member functions themselves, and this can prevent other special member functions from being automatically generated. In practice:

  • If any constructor is explicitly declared, then no default constructor is automatically generated.

  • If a virtual destructor is explicitly declared, then no default destructor is automatically generated.

  • If a move constructor or move-assignment operator is explicitly declared, then:

    • No copy constructor is automatically generated.

    • No copy-assignment operator is automatically generated.

  • If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then:

    • No move constructor is automatically generated.

    • No move-assignment operator is automatically generated.

Additionally, the C++11 standard specifies the following additional rules:

  • If a copy constructor or destructor is explicitly declared, then automatic generation of the copy-assignment operator is deprecated.
  • If a copy-assignment operator or destructor is explicitly declared, then automatic generation of the copy constructor is deprecated.

In both cases, Visual Studio continues to automatically generate the necessary functions implicitly, and does not emit a warning.

The consequences of these rules can also leak into object hierarchies. For example, if for any reason a base class fails to have a default constructor that's callable from a deriving class—that is, a or constructor that takes no parameters—then a class that derives from it cannot automatically generate its own default constructor.

These rules can complicate the implementation of what should be straight-forward, user-defined types and common C++ idioms—for example, making a user-defined type non-copyable by declaring the copy constructor and copy-assignment operator privately and not defining them.

Before C++11, this code snippet was the idiomatic form of non-copyable types. However, it has several problems:

  • The copy constructor has to be declared privately to hide it, but because it’s declared at all, automatic generation of the default constructor is prevented. You have to explicitly define the default constructor if you want one, even if it does nothing.

  • Even if the explicitly-defined default constructor does nothing, it's considered non-trivial by the compiler. It's less efficient than an automatically generated default constructor and prevents from being a true POD type.

  • Even though the copy constructor and copy-assignment operator are hidden from outside code, the member functions and friends of can still see and call them. If they are declared but not defined, calling them causes a linker error.

  • Although this is a commonly accepted idiom, the intent is not clear unless you understand all of the rules for automatic generation of the special member functions.

In C++11, the non-copyable idiom can be implemented in a way that is more straightforward.

Notice how the problems with the pre-C++11 idiom are resolved:

  • Generation of the default constructor is still prevented by declaring the copy constructor, but you can bring it back by explicitly defaulting it.

  • Explicitly defaulted special member functions are still considered trivial, so there is no performance penalty, and is not prevented from being a true POD type.

  • The copy constructor and copy-assignment operator are public but deleted. It is a compile-time error to define or call a deleted function.

  • The intent is clear to anyone who understands and . You don't have to understand the rules for automatic generation of special member functions.

Similar idioms exist for making user-defined types that are non-movable, that can only be dynamically allocated, or that cannot be dynamically allocated. Each of these idioms have pre-C++11 implementations that suffer similar problems, and that are similarly resolved in C++11 by implementing them in terms of defaulted and deleted special member functions.

You can default any of the special member functions—to explicitly state that the special member function uses the default implementation, to define the special member function with a non-public access qualifier, or to reinstate a special member function whose automatic generation was prevented by other circumstances.

You default a special member function by declaring it as in this example:

Notice that you can default a special member function outside the body of a class as long as it’s inlinable.

Because of the performance benefits of trivial special member functions, we recommend that you prefer automatically generated special member functions over empty function bodies when you want the default behavior. You can do this either by explicitly defaulting the special member function, or by not declaring it (and also not declaring other special member functions that would prevent it from being automatically generated.)

Visual Studio does not support defaulted move constructors or move-assignment operators as the C++11 standard mandates. For more information, see the Defaulted and Deleted functions section of Support For C++11/14/17 Features (Modern C++).

You can delete special member functions as well as normal member functions and non-member functions to prevent them from being defined or called. Deleting of special member functions provides a cleaner way of preventing the compiler from generating special member functions that you don’t want. The function must be deleted as it is declared; it cannot be deleted afterwards in the way that a function can be declared and then later defaulted.

Deleting of normal member function or non-member functions prevents problematic type promotions from causing an unintended function to be called. This works because deleted functions still participate in overload resolution and provide a better match than the function that could be called after the types are promoted. The function call resolves to the more-specific—but deleted—function and causes a compiler error.

Notice in the preceding sample that calling by using a argument would cause a compiler error, but calling by using an argument would not; in the case, the argument will be promoted from to and successfully call the version of the function, even though that might not be what’s intended. To ensure that any call to this function by using a non-double argument causes a compiler error, you can declare a template version of the function that’s deleted.

struct noncopyable { noncopyable() {}; private: noncopyable(const noncopyable&); noncopyable& operator=(const noncopyable&); };
struct noncopyable { noncopyable() =default; noncopyable(const noncopyable&) =delete; noncopyable& operator=(const noncopyable&) =delete; };
struct widget { widget()=default; inline widget& operator=(const widget&); }; inline widget& widget::operator=(const widget&) =default;
struct widget { // deleted operator new prevents widget from being dynamically allocated. void* operator new(std::size_t) = delete; };
// deleted overload prevents call through type promotion of float to double from succeeding. void call_with_true_double_only(float) =delete; void call_with_true_double_only(double param) { return; }
template < typename T > void call_with_true_double_only(T) =delete; //prevent call through type promotion of any T to double from succeeding. void call_with_true_double_only(double param) { return; } // also define for const double, double&, etc. as needed.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *