C++

Special Member Functions

Virtual and Protected Destructors

A class designed to be inherited-from is called a Base class. Care should be taken with the special member functions of such a class.

A class designed to be used polymorphically at run-time (through a pointer to the base class) should declare the destructor virtual. This allows the derived parts of the object to be properly destroyed, even when the object is destroyed through a pointer to the base class.

class Base {
public:
    virtual ~Base() = default;

private:
    //    data members etc.
};

class Derived : public Base { //  models Is-A relationship
public:
    //    some methods

private:
    //    more data members
};

//    virtual destructor in Base ensures that derived destructors
//    are also called when the object is destroyed
std::unique_ptr<Base> base = std::make_unique<Derived>();
base = nullptr;  //    safe, doesn't leak Derived's members

If the class does not need to be polymorphic, but still needs to allow its interface to be inherited, use a non-virtual protected destructor.

class NonPolymorphicBase {
public:
    //    some methods

protected:
    ~NonPolymorphicBase() = default; //    note: non-virtual

private:
    //    etc.
};

Such a class can never be destroyed through a pointer, avoiding silent leaks due to slicing.

This technique especially applies to classes designed to be private base classes. Such a class might be used to encapsulate some common implementation details, while providing virtual methods as customisation points. This kind of class should never be used polymorphically, and a protected destructor helps to document this requirement directly in the code.

Finally, some classes may require that they are never used as a base class. In this case, the class can be marked final. A normal non-virtual public destructor is fine in this case.

class FinalClass final {  //    marked final here
public:
    ~FinalClass() = default;

private:
    //    etc.
};

Implicit Move and Copy

Bear in mind that declaring a destructor inhibits the compiler from generating implicit move constructors and move assignment operators. If you declare a destructor, remember to also add appropriate definitions for the move operations.

Furthermore, declaring move operations will suppress the generation of copy operations, so these should also be added (if the objects of this class are required to have copy semantics).

class Movable {
public:
    virtual ~Movable() noexcept = default;

    //    compiler won't generate these unless we tell it to
    //    because we declared a destructor
    Movable(Movable&&) noexcept = default;
    Movable& operator=(Movable&&) noexcept = default;

    //    declaring move operations will suppress generation
    //    of copy operations unless we explicitly re-enable them
    Movable(const Movable&) = default;
    Movable& operator=(const Movable&) = default;
};

Copy and swap

If you’re writing a class that manages resources, you need to implement all the special member functions (see Rule of Three/Five/Zero). The most direct approach to writing the copy constructor and assignment operator would be:

person(const person &other)
    : name(new char[std::strlen(other.name) + 1])
    , age(other.age)
{
    std::strcpy(name, other.name);
}

person& operator=(person const& rhs) {
    if (this != &other) {
        delete [] name;
        name = new char[std::strlen(other.name) + 1];
        std::strcpy(name, other.name);
        age = other.age;
    }

    return *this;
}

But this approach has some problems. It fails the strong exception guarantee - if new[] throws, we’ve already cleared the resources owned by this and cannot recover. We’re duplicating a lot of the logic of copy construction in copy assignment. And we have to remember the self-assignment check, which usually just adds overhead to the copy operation, but is still critical.

To satisfy the strong exception guarantee and avoid code duplication (double so with the subsequent move assignment operator), we can use the copy-and-swap idiom:

class person {
    char* name;
    int age;
public:
    /* all the other functions ... */

    friend void swap(person& lhs, person& rhs) {
        using std::swap; // enable ADL

        swap(lhs.name, rhs.name);
        swap(lhs.age, rhs.age);
    }

    person& operator=(person rhs) {
        swap(*this, rhs);
        return *this;
    }
};

Why does this work? Consider what happens when we have

person p1 = ...;
person p2 = ...;
p1 = p2;

First, we copy-construct rhs from p2 (which we didn’t have to duplicate here). If that operation throws, we don’t do anything in operator= and p1 remains untouched. Next, we swap the members between *this and rhs, and then rhs goes out of scope. When operator=, that implicitly cleans the original resources of this (via the destructor, which we didn’t have to duplicate). Self-assignment works too - it’s less efficient with copy-and-swap (involves an extra allocation and deallocation), but if that’s the unlikely scenario, we don’t slow down the typical use case to account for it.

The above formulation works as-is already for move assignment.

p1 = std::move(p2);

Here, we move-construct rhs from p2, and all the rest is just as valid. If a class is movable but not copyable, there is no need to delete the copy-assignment, since this assignment operator will simply be ill-formed due to the deleted copy constructor.

Default Constructor

A default constructor is a type of constructor that requires no parameters when called. It is named after the type it constructs and is a member function of it (as all constructors are).

class C{
    int i;
public:
    // the default constructor definition
    C()
    : i(0){ // member initializer list -- initialize i to 0
        // constructor function body -- can do more complex things here
    }
};

C c1; // calls default constructor of C to create object c1
C c2 = C(); // calls default constructor explicitly
C c3(); // ERROR: this intuitive version is not possible due to "most vexing parse"
C c4{}; // but in C++11 {} CAN be used in a similar way

C c5[2]; // calls default constructor for both array elements
C* c6 = new C[2]; // calls default constructor for both array elements

Another way to satisfy the “no parameters” requirement is for the developer to provide default values for all parameters:

class D{
    int i;
    int j;
public:
    // also a default constructor (can be called with no parameters)
    D( int i = 0, int j = 42 ) 
    : i(i), j(j){
    }
};


D d; // calls constructor of D with the provided default values for the parameters

Under some circumstances (i.e., the developer provides no constructors and there are no other disqualifying conditions), the compiler implicitly provides an empty default constructor:

class C{
    std::string s; // note: members need to be default constructible themselves
};

C c1; // will succeed -- C has an implicitly defined default constructor

Having some other type of constructor is one of the disqualifying conditions mentioned earlier:

class C{
    int i;
public:
    C( int i ) : i(i){}
};

C c1; // Compile ERROR: C has no (implicitly defined) default constructor

To prevent implicit default constructor creation, a common technique is to declare it as private (with no definition). The intention is to cause a compile error when someone tries to use the constructor (this either results in an Access to private error or a linker error, depending on the compiler).

To be sure a default constructor (functionally similar to the implicit one) is defined, a developer could write an empty one explicitly.

In C++11, a developer can also use the delete keyword to prevent the compiler from providing a default constructor.

class C{
    int i;
public:
    // default constructor is explicitly deleted
    C() = delete;
};

C c1; // Compile ERROR: C has its default constructor deleted

Furthermore, a developer may also be explicit about wanting the compiler to provide a default constructor.

class C{
    int i;
public:
    // does have automatically generated default constructor (same as implicit one)
    C() = default;

    C( int i ) : i(i){}
};

C c1; // default constructed
C c2( 1 ); // constructed with the int taking constructor 

You can determine whether a type has a default constructor (or is a primitive type) using std::is_default_constructible from <type_traits>:

class C1{ };
class C2{ public: C2(){} };
class C3{ public: C3(int){} };

using std::cout; using std::boolalpha; using std::endl;
using std::is_default_constructible;
cout << boolalpha << is_default_constructible<int>() << endl; // prints true
cout << boolalpha << is_default_constructible<C1>() << endl; // prints true
cout << boolalpha << is_default_constructible<C2>() << endl; // prints true
cout << boolalpha << is_default_constructible<C3>() << endl; // prints false

In C++11, it is still possible to use the non-functor version of std::is_default_constructible:

cout << boolalpha << is_default_constructible<C1>::value << endl; // prints true

Destructor

A destructor is a function without arguments that is called when a user-defined object is about to be destroyed. It is named after the type it destructs with a ~ prefix.

class C{
    int* is;
    string s;
public:
    C()
    : is( new int[10] ){
    }

    ~C(){  // destructor definition
        delete[] is;
    }
};

class C_child : public C{
    string s_ch;
public:
    C_child(){}
    ~C_child(){} // child destructor
};

void f(){
    C c1; // calls default constructor
    C c2[2]; // calls default constructor for both elements
    C* c3 = new C[2]; // calls default constructor for both array elements

    C_child c_ch;  // when destructed calls destructor of s_ch and of C base (and in turn s)

    delete[] c3; // calls destructors on c3[0] and c3[1]
} // automatic variables are destroyed here -- i.e. c1, c2 and c_ch

Under most circumstances (i.e., a user provides no destructor, and there are no other disqualifying conditions), the compiler provides a default destructor implicitly:

class C{
    int i;
    string s;
};

void f(){
    C* c1 = new C;
    delete c1; // C has a destructor
}

class C{
    int m;
private:
    ~C(){} // not public destructor!
};

class C_container{
    C c;
};

void f(){
    C_container* c_cont = new C_container;
    delete c_cont; // Compile ERROR: C has no accessible destructor
}

In C++11, a developer can override this behavior by preventing the compiler from providing a default destructor.

class C{
    int m;
public:
    ~C() = delete; // does NOT have implicit destructor
};

void f{
    C c1; 
} // Compile ERROR: C has no destructor

Furthermore, a developer may also be explicit about wanting the compiler to provide a default destructor.

class C{
    int m;
public:
    ~C() = default; // saying explicitly it does have implicit/empty destructor
};

void f(){
    C c1;
} // C has a destructor -- c1 properly destroyed

You can determine whether a type has a destructor (or is a primitive type) using std::is_destructible from <type_traits>:

class C1{ };
class C2{ public: ~C2() = delete };
class C3 : public C2{ };

using std::cout; using std::boolalpha; using std::endl;
using std::is_destructible;
cout << boolalpha << is_destructible<int>() << endl; // prints true
cout << boolalpha << is_destructible<C1>() << endl; // prints true
cout << boolalpha << is_destructible<C2>() << endl; // prints false
cout << boolalpha << is_destructible<C3>() << endl; // prints false

This modified text is an extract of the original Stack Overflow Documentation created by the contributors and released under CC BY-SA 3.0 This website is not affiliated with Stack Overflow