Rule of three (C ++)

from Wikipedia, the free encyclopedia

The rule of three describes a rule of thumb in the C ++ programming language that recommends that in a class that defines one of the following three member functions, the other two should also be defined:

  1. Copy constructor
  2. Destructor
  3. Assignment operator

background

These three member functions are usually generated automatically by the compiler. These generated versions have a meaning defined in the language standard: All non-static data elements are copied in the order in which they were declared (1 and 3) or released / cleared in the reverse order (2).

However, if a class has different semantics, e.g. B. because it contains a resource as a data element that can not be copied or cleared in this way, each of these three member functions can be replaced by its own definition. In most cases, such classes then require all three of these member functions to have their own custom implementation.

Examples

Resources about handles

If a data element represents a resource (e.g. a file, TCP or database connection) via a handle , this meaning is i. d. Usually not known. The data element is e.g. B. of the type int. So that the represented resource is closed or released in the destructor of the class, the class must have a user-defined destructor in which the resource is explicitly closed / released via a system call.

Likewise, the copy constructor and assignment operator must do more than just copy the handle so that the original object and copy can access the resource without conflict. If the resource cannot or should not be accessed from several objects, both member functions must be explicitly deleted, which means that an object of this type cannot be copied:

class Datei
{
public:
    Datei(const char* dateiname)
    : file(fopen(dateiname, "rb"))
    { /* Fehlerbehandlung usw. */ }

    // Dreierregel:
    Datei(const Datei&) = delete; // Kein Kopieren!
    ~Datei() { fclose(file); }
    void operator=(const Datei&) = delete; // Kein Kopieren!

    // weitere Elementfunktionen
    // ...

private:
    FILE* file;
};

Resource about "bare" pointers

It is similar when a resource is referenced via a "bare pointer". The compiler-generated functions copy the pointer, but not the resource referenced by it. This is also called a flat copy . This means that the original object and the copy share the resource. In addition, it is not clear to which of the objects the shared resource “belongs”, i.e. who is responsible for clearing the resource.

So the class needs user-defined definitions for the copy constructor and the assignment operator. In these, either the referenced resource has to be explicitly duplicated ( deep copy ) or the access has to be regulated in another way, if necessary the only sensible solution here is to prohibit the copying of objects of this class by explicitly deleting these element functions.

Modern compilers offer the option of issuing a warning if a class is defined that contains “bare pointers” as data elements but does not meet the rule of three.

Another possibility is the use of smart pointers , which encapsulate the referenced resource in a defined way and clearly regulate the access and the service life of a possibly shared resource. The C ++ standard library has provided its own smart pointer classes since C ++ 11 :

Smart pointer class meaning
std :: unique_ptr <T> Resource cannot be copied implicitly. The compiler returns an error when attempting to copy an object of a class that has unique_ptrdata elements but no user-defined copy functions.
std :: shared_ptr <T> The referenced resource is shared between the original and target objects. The number of copies is recorded via a counter so that the resource can be cleared / released when the service life of the last copy ends.
std :: weak_ptr <T>

unique_ptrand also shared_ptrcontain an optional "Deleter" function if the referenced resource cannot simply be deletereleased via .

Exceptions to the rule of three

The rule of three is not a rule that must be followed without exception, but rather a rule of thumb or recommendation. A specific example in which the rule of three does not have to be applied is a reference counter: When creating (standard constructor), copying (copy constructor) and destroying (destructor) the number of objects changes, but not when assigning:

struct RefCounter
{
	static std::size_t n;

	RefCounter() // Standardkonstruktor
	{
		++n;
	}
	RefCounter(RefCounter const&) // Kopierkonstruktor
	{
		++n;
	}
	~RefCounter() // Destruktor
	{
		--n;
	}

	// Kein Zuweisungsoperator benötigt
	// RefCounter& operator= (RefCounter const&)
};

Rule of five

Since the appearance of C ++ 11 , this rule has become a rule of five , including

should be defined.

On the other hand, the responsibilities of the classes should be separated:

  1. Classes that each hold exactly one resource. The rule of three or five generally applies to these, but only a few of these resource management classes are required. The existing smart pointers in the standard library can also often be used for this.
  2. Classes that simply aggregate other resources in their data members. These classes then do not need any user-defined copy functions or destructors, since the compiler-generated functions then automatically contain the correct semantics. This simplifies the implementation and testing of these classes considerably.

This approach is also called the rule of zero because the vast majority of classes belong to the second category with no user-defined copy functions and destructors.

Since C ++ 11 it is also possible not only to suppress the creation of the compiler-generated version, but also to force it explicitly ( =default). This tells the compiler (and also the human reader) that in this case the compiler-generated version offers exactly the desired behavior, so that it does not have to be implemented manually:

class Example
{
    Example(const Example&) = default;  // erzwinge compilergenerierte Version
    void operator=(const Example&) = delete; // verhindere compilergenerierte Version
};

literature

  • Stanley B. Lippman, Josèe Lajoie, Barbara E. Moo: C ++ Primer. 4th edition. Addison-Wesley Professional, 2005, ISBN 0-201-72148-1 .

Individual evidence

  1. ^ Bjarne Stroustrup : The C ++ Programming Language . 3. Edition. Addison-Wesley, 2000, ISBN 0-201-70073-5 , pp. 283-284 .
  2. Proposing the Rule of Five (PDF)
  3. Proposing the Rule of Five, v2 (PDF)
  4. en.cppreference.com