📜 ⬆️ ⬇️

So, you decided to prohibit copying class objects in C ++.

SHALL NOT DANCE THERE Quite often, you can find C ++ code in which one or more classes have the copy constructor and the assignment operator declared private and a comment like "copying is prohibited" is written.

Reception is simple and seemingly obvious, however, with its use, pitfalls are possible, leading to errors that will not appear soon and the search for which can take more than one day.

Consider possible problems.

First - a brief excursion why this technique is needed.
')
If a program tries to copy a class object, the default C ++ compiler automatically generates a copy constructor or an assignment operator if they are not explicitly declared in the class. The automatically generated constructor performs term-by-term copying, and the automatically-generated assignment operator performs the term-by-term assignment.

Here is a simple example:

class DoubleDelete { public: DoubleDelete() { ptr = new char[100]; } ~DoubleDelete() { delete[] ptr; } private: char* ptr; }; 

In this code:
 { DoubleDelete first; DoubleDelete second( first ); } 

uncertain behavior will occur. The copy constructor generated by the compiler will be called, which will copy the pointer. As a result, both objects will store pointers with equal addresses. The first will execute the second object's destructor, it will execute delete [], then the first object's destructor will be called, it will try to delete [] again for the same address, and this will lead to undefined behavior.

The solution seems to be obvious - implement a copy constructor and an assignment operator with the correct behavior. For example, when copying, a new object creates its own array and copies data from the old one into it.

This is not always the right way. Not all objects are inherently copyable.

For example, an object might store an open file descriptor as an integer. The destructor "closes the file" using the function of the operating system. Obviously, textual copying will not work. And what should happen when copying? Should the file open again? Usually in this case, copying does not make sense.

Another example is a class for capturing a critical section when creating an object of such a class. What is the point of copying an object? Section already captured.

A brief digression on this is completed, proceed to an attempt to portray the decision.

If copying an object does not make sense, you need to make sure that the compiler cannot accidentally execute it. To do this, usually do this:

 // NOT BAD class NonCopyable { // blahblahblahpublic: private: // copy and assignment prohibited NonCopyable( const NonCopyable& ); void NonCopyable::operator=( const NonCopyable& ); }; 

or so:

 // FAIL class NonCopyable { // blahblahblahpublic: private: // copy and assignment prohibited NonCopyable( const NonCopyable& ) { assert( false ); } void NonCopyable::operator=( const NonCopyable& ) { assert( false ); } }; 

or so:

 // EPIC FAIL class NonCopyable { // blahblahblahpublic: private: // copy and assignment prohibited NonCopyable( const NonCopyable& ) {} void NonCopyable::operator=( const NonCopyable& ) {} }; 

All three methods are found in real code.

It would seem that the second and third options are different from the first? The private modifier, in any case, will not cause a copy.

EXTREMELY UNEXPECTED ...

Member functions of the same class can call the copy constructor and the assignment operator, even if they are declared private. And the "friends" of the class (friend) can also. Nobody bothers to write something like this in the code:

 NonCopyable NonCopyable::SomeMethod() { // blahblahblah return *this; } 

or such:

 void NonCopyable::SomeOtherMehod() { callSomething( *this ); } 

Now there is a difference between the first option and the rest.

The first option (no implementation) will lead to an error during the build program. The error message is not the most understandable, but at least reliable.

In the second variant, an assert will be triggered ... provided that the control passes through this code. Here a lot depends on how often this code is called, in particular, on the code coverage by tests. Maybe you will notice a problem at the first start, maybe - very soon.

In the third variant, it is even better - the assignment operator does not change the object, and the copy constructor calls the default constructors of all members of the class. Wide room for error, it may be even more difficult to notice than the second.

Expected objection - once the copy constructor and the assignment operator are declared but not defined, they can be mistakenly or maliciously defined anywhere in the code. This problem is solved very simply.

From the error helps comment like "prohibited operations" or, if in doubt, "prohibited operations, not to determine under the fear of dismissal." Nothing helps you from malicious intent - in C ++ no one bothers to take the address of an object, cast it to type char * and rewrite the object as you like, byte-by-byte.

C ++ 0x has the delete keyword:

 // C++0x OPTIMAL class NonCopyable { private: // copy and assignment not allowed NonCopyable( const NonCopyable& ) = delete; void operator=( const NonCopyable& ) = delete; // superior developers wanted – www.abbyy.ru/vacancy }; 

In this case, not only to determine but also to call them will be impossible - when you try to compile the place of the call, you will receive a compilation error.

The option “declare and not define” is also available in earlier C ++ 0x, in particular, it uses boost :: noncopyable. The option to inherit from boost :: noncopyable or a similar class is also quite reliable and available in any version.

The attentive reader probably noticed that in all the examples above the assignment operator returns void, and not a link to the same class. This is done specifically to design

 first = second = third; 

caused a compilation error in C ++ 03.

So small code improvements sometimes help to avoid the Darwin Award.

Dmitry Mescheryakov,
product department for developers

Source: https://habr.com/ru/post/142595/


All Articles