Поделиться через


Constructors (C++)

A constructor is a special member function that initializes an instance of its class. To call a constructor, you use the class name together with parameters surrounded by braces or parentheses.

class Box {
public:
    Box(int width, int length, int height){
        m_width = width;
        m_length = length;
        m_height = height;
    }

private:
    int m_width;
    int m_length;
    int m_height;

};
class SomeClass{
public:
    static void set_box(const Box& aBox) {}
};
int main(){

    Box box1(1, 2, 3);
    Box box2{ 2, 3, 4 };
    SomeClass::set_box(Box{ 5, 6, 7 });
}

For more information, see Rules for Declaring Constructors. For information about initialization, see Initialization.

Order of Construction

A constructor performs its work in this order:

  1. It calls base class and member constructors in the order of declaration.

  2. If the class is derived from virtual base classes, it initializes the object's virtual base pointers.

  3. If the class has or inherits virtual functions, it initializes the object's virtual function pointers. Virtual function pointers point to the class's virtual function table to enable correct binding of virtual function calls to code.

  4. It executes any code in its function body.

The following example shows the order in which base class and member constructors are called in the constructor for a derived class. First, the base constructor is called, then the base-class members are initialized in the order in which they appear in the class declaration, and then the derived constructor is called.

#include <iostream>
using namespace std;

class Contained1 {
public:
    Contained1() {
        cout << "Contained1 constructor." << endl;
    }
};

class Contained2 {
public:
    Contained2() {
        cout << "Contained2 constructor." << endl;
    }
};

class Contained3 {
public:
    Contained3() {
        cout << "Contained3 constructor." << endl;
    }
};

class BaseContainer {
public:
    BaseContainer() {
        cout << "BaseContainer constructor." << endl;
    }
private:
    Contained1 c1;
    Contained2 c2;
};

class DerivedContainer : public BaseContainer {
public:
    DerivedContainer() : BaseContainer() {
        cout << "DerivedContainer constructor." << endl;
    }
private:
    Contained3 c3;
};

int main() {
    DerivedContainer dc;
    int x = 3;
}

Here's the output:

Contained1 constructor.
Contained2 constructor.
BaseContainer constructor.
Contained3 constructor.
DerivedContainer constructor.

If a constructor throws an exception, the order of destruction is the reverse of the order of construction:

  1. The code in the body of the constructor function is unwound.

  2. Base class and member objects are destroyed, in the reverse order of declaration.

  3. If the constructor is non-delegating, all fully-constructed base class objects and members are destroyed. However, because the object itself is not fully constructed, the destructor is not run.

Explicit Constructors

Using the explicit keyword on a constructor can prevent implicit type conversions if the constructor has only one parameter or if all parameters except one have a default value. For more information, see Constructors (C++).

Default Constructors

Default constructors—that is, constructors that don't have parameters—follow slightly different rules.

If no constructors are declared in a class, the compiler provides a default constructor:

class Box {
    int m_width;
    int m_length;
    int m_height;
};

int main(){

    Box box1{};
    Box box2;
}

When you call a default constructor and try to use parentheses, a warning is issued:

class myclass{};
int main(){
myclass mc();     // warning C4930: prototyped function not called (was a variable definition intended?)
}

This is an example of the Most Vexing Parse problem. Because the example expression can be interpreted either as the declaration of a function or as the invocation of a default constructor, and because C++ parsers favor declarations over other things, the expression is treated as a function declaration. For more information, see Most Vexing Parse.

If any non-default constructors are declared, the compiler does not provide a default constructor:

class Box {
public:
    Box(int width, int length, int height){
       m_width = width;
       m_length = length;
       m_height = height;
    }

private:
    int m_width;
    int m_length;
    int m_height;

};

int main(){

    Box box1(1, 2, 3);
    Box box2{ 2, 3, 4 };
    Box box4;     // compiler error C2512: no appropriate default constructor available
}

If a class has no default constructor, an array of objects of that class cannot be constructed by using square-bracket syntax alone. For example, given the previous code block, an array of Boxes cannot be declared like this:

Box boxes[3];    // compiler error C2512: no appropriate default constructor available

However, you can use a set of initializer lists to initialize an array of Boxes:

Box boxes[3]{ { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

Copy and Move Constructors

A copy constructor uses a reference to an object to make a copy of it. If you don't supply a copy constructor for a class, the compiler creates a default one, even when a copy operation, move operation, or destructor has been declared. For more information, see Rules for Declaring Constructors.

A move constructor enables the transfer of allocated memory from one object to another. For more information, see How to: Write a Move Constructor.

Explicitly Defaulted and Deleted Constructors

You can explicitly default copy constructors, default constructors, copy assignment operators, and destructors, but explicit default of move constructors and move assignment operators is not supported. (This support is present in Visual Studio 2015.) You can explicitly delete all of the special functions. For more information, see Explicitly Defaulted and Deleted Functions.

Constructors in Derived Classes

A derived class constructor always calls a base class constructor, so that it can rely on completely constructed base classes before any extra work is done. The base class constructors are called in order of derivation—for example, if ClassA is derived from ClassB, which is derived from ClassC, the ClassC constructor is called first, then the ClassB constructor, then the ClassA constructor.

If a base class does not have a default constructor, you must supply the base class constructor parameters in the derived class constructor:

class Box {
public:
    Box(int width, int length, int height){
       m_width = width;
       m_length = length;
       m_height = height;
    }

private:
    int m_width;
    int m_length;
    int m_height;
};

class StorageBox : public Box {
public:
    StorageBox(int width, int length, int height, const string label&) : Box(width, length, height){
        m_label = label;
    }
private:
    string m_label;
};

int main(){

    const string aLabel = "aLabel";
    StorageBox sb(1, 2, 3, aLabel);
} 

Constructors for Classes That Have Multiple Inheritance

If a class is derived from multiple base classes, the base class constructors are invoked in the order in which they are listed in the declaration of the derived class:

#include <iostream>
using namespace std;

class BaseClass1 {
public:
    BaseClass1() {
        cout << "BaseClass1 constructor." << endl;
    }
};
class BaseClass2 {
public:
    BaseClass2() {
        cout << "BaseClass2 constructor." << endl;
    }
};
class BaseClass3{
public:
    BaseClass3() {
        cout << "BaseClass3 constructor." << endl;
    }
};
class DerivedClass : public BaseClass1, public BaseClass2, public BaseClass3  {
public:
    DerivedClass() {
        cout << "DerivedClass constructor." << endl;
    }
};

int main() {
    DerivedClass dc;
}

You should expect the following output:

BaseClass1 constructor.
BaseClass2 constructor.
BaseClass3 constructor.
DerivedClass constructor.

Virtual Functions in Constructors

We recommend that you be careful when you call virtual functions in constructors. Because the base class constructor is always invoked before the derived class constructor, the function that's called in the base constructor is the base class version, not the derived class version. In the following example, constructing a DerivedClass causes the BaseClass implementation of print_it() to execute before the DerivedClass constructor causes the DerivedClass implementation of print_it() to execute:

#include <iostream>
using namespace std;

class BaseClass{
public:
    BaseClass(){
        print_it();
    }
    virtual void print_it() {
        cout << "BaseClass print_it" << endl;
    }
};

class DerivedClass : public BaseClass {
public:
    DerivedClass() {
        print_it();
    }
    virtual void print_it(){
        cout << "Derived Class print_it" << endl;
    }
};

int main() {

    DerivedClass dc;
}

Here's the output:

BaseClass print_it
Derived Class print_it

Constructors and Composite Classes

Classes that contain class-type members are known as composite classes. When a class-type member of a composite class is created, the constructor is called before the class's own constructor. When a contained class lacks a default constructor, you must use an initialization list in the constructor of the composite class. In the earlier StorageBox example, if you change the type of the m_label member variable to a new Label class, you must call both the base class constructor and initialize the m_label variable in the StorageBox constructor:

class Label {
public:
    Label(const string& name, const string& address) { m_name = name; m_address = address; }
    string m_name;
    string m_address;
};

class StorageBox : public Box {
public:
    StorageBox(int width, int length, int height, Label label) 
        : Box(width, length, height), m_label(label){}
private:
    Label m_label;
};

int main(){
// passing a named Label
    Label label1{ "some_name", "some_address" };
    StorageBox sb1(1, 2, 3, label1);

    // passing a temporary label
    StorageBox sb2(3, 4, 5, Label{ "another name", "another address" });

    // passing a temporary label as an initializer list
    StorageBox sb3(1, 2, 3, {"myname", "myaddress"});
}

Delegating Constructors

A delegating constructor calls a different constructor in the same class to do some of the work of initialization. In the following example, the derived class has three constructors—the second constructor delegates to the first, and the third constructor delegates to the second:

#include <iostream>
using namespace std;

class ConstructorDestructor {
public:
    ConstructorDestructor() {
        cout << "ConstructorDestructor default constructor." << endl;
    }
    ConstructorDestructor(int int1) {
        cout << "ConstructorDestructor constructor with 1 int." << endl;
    }
    ConstructorDestructor(int int1, int int2) : ConstructorDestructor(int1) {
        cout << "ConstructorDestructor constructor with 2 ints." << endl;
        
        throw exception();
    }
    ConstructorDestructor(int int1, int int2, int int3) : ConstructorDestructor(int1, int2) {
        cout << "ConstructorDestructor constructor with 3 ints." << endl;
    }
    ~ConstructorDestructor() {
        cout << "ConstructorDestructor destructor." << endl;
    }
};

int main() {
    ConstructorDestructor dc(1, 2, 3);
}

Here's the output:

ConstructorDestructor constructor with 1 int.
ConstructorDestructor constructor with 2 ints.
ConstructorDestructor constructor with 3 ints.

The object created by the constructors is fully initialized as soon as any constructor is finished. DerivedContainer(int int1) succeeds, but DerivedContainer(int int1, int int2) fails and the destructor is called.

class ConstructorDestructor {
public:
    ConstructorDestructor() {
        cout << "ConstructorDestructor default constructor." << endl;
    }
    ConstructorDestructor(int int1) {
        cout << "ConstructorDestructor constructor with 1 int." << endl;
    }
    ConstructorDestructor(int int1, int int2) : ConstructorDestructor(int1) {
        cout << "ConstructorDestructor constructor with 2 ints." << endl;
        throw exception();
    }
    ConstructorDestructor(int int1, int int2, int int3) : ConstructorDestructor(int1, int2) {
        cout << "ConstructorDestructor constructor with 3 ints." << endl;
    }

    ~ConstructorDestructor() {
        cout << "ConstructorDestructor destructor." << endl;
    }
};

int main() {

    try {
        ConstructorDestructor cd{ 1, 2, 3 };
    }
    catch (const exception& ex){
    }
}

Output:

ConstructorDestructor constructor with 1 int.
ConstructorDestructor constructor with 2 ints.
ConstructorDestructor destructor.

For more information, see Uniform Initialization and Delegating Constructors.

See Also

Reference

Special Member Functions (C++)