Konstruktory (C++)

Aby dostosować sposób inicjowania jej składowych lub wywoływania funkcji podczas tworzenia obiektu klasy, zdefiniuj konstruktor. Konstruktor ma taką samą nazwę jak klasa i żadna wartość zwracana. Można zdefiniować dowolną liczbę przeciążonych konstruktorów w razie potrzeby w celu dostosowania inicjalizacji na różne sposoby. Zazwyczaj konstruktory mają dostęp publiczny, dzięki czemu kod poza definicją klasy lub hierarchią dziedziczenia może tworzyć obiekty klasy. Można jednak również zadeklarować konstruktor jako protected lub private.

Konstruktory mogą opcjonalnie przyjmować listę inicjatorów składowych. Jest to bardziej wydajny sposób inicjowania składowych klasy niż przypisywania wartości w treści konstruktora. W poniższym przykładzie przedstawiono klasę Box z trzema przeciążonym konstruktorami. Ostatnie dwie listy inicjowania elementu członkowskiego:

class Box {
public:
    // Default constructor
    Box() {}

    // Initialize a Box with equal dimensions (i.e. a cube)
    explicit Box(int i) : m_width(i), m_length(i), m_height(i) // member init list
    {}

    // Initialize a Box with custom dimensions
    Box(int width, int length, int height)
        : m_width(width), m_length(length), m_height(height)
    {}

    int Volume() { return m_width * m_length * m_height; }

private:
    // Will have value of 0 when default constructor is called.
    // If we didn't zero-init here, default constructor would
    // leave them uninitialized with garbage values.
    int m_width{ 0 };
    int m_length{ 0 };
    int m_height{ 0 };
};

Podczas deklarowania wystąpienia klasy kompilator wybiera konstruktor, który ma być wywoływany na podstawie reguł rozpoznawania przeciążenia:

int main()
{
    Box b; // Calls Box()

    // Using uniform initialization (preferred):
    Box b2 {5}; // Calls Box(int)
    Box b3 {5, 8, 12}; // Calls Box(int, int, int)

    // Using function-style notation:
    Box b4(2, 4, 6); // Calls Box(int, int, int)
}
  • Konstruktory mogą być deklarowane jako inline, , explicitfriendlub constexpr.
  • Konstruktor może zainicjować obiekt, który został zadeklarowany jako const, volatile lub const volatile. Obiekt staje się const po zakończeniu konstruktora.
  • Aby zdefiniować konstruktor w pliku implementacji, nadaj mu kwalifikowaną nazwę jak dowolna inna funkcja składowa: Box::Box(){...}.

Listy inicjatorów składowych

Konstruktor może opcjonalnie mieć listę inicjatorów składowych, która inicjuje składowe klasy przed uruchomieniem treści konstruktora. (Lista inicjatorów składowych nie jest taka sama jak lista inicjatora typu std::initializer_list<T>).

Preferuj listy inicjatorów składowych w przypadku przypisywania wartości w treści konstruktora. Lista inicjatorów składowych bezpośrednio inicjuje członków. W poniższym przykładzie pokazano listę inicjatora składowego, która składa się ze wszystkich identifier(argument) wyrażeń po dwukropku:

    Box(int width, int length, int height)
        : m_width(width), m_length(length), m_height(height)
    {}

Identyfikator musi odwoływać się do składowej klasy; jest inicjowany z wartością argumentu. Argument może być jednym z parametrów konstruktora, wywołaniem funkcji lub .std::initializer_list<T>

const członkowie i składowe typu odwołania muszą być inicjowane na liście inicjatorów składowych.

Aby upewnić się, że klasy bazowe są w pełni inicjowane przed uruchomieniem konstruktora pochodnego, wywołaj wszystkie sparametryzowane konstruktory klas bazowych na liście inicjatorów.

Konstruktory domyślne

Konstruktory domyślne zwykle nie mają parametrów, ale mogą mieć parametry z wartościami domyślnymi.

class Box {
public:
    Box() { /*perform any required default initialization steps*/}

    // All params have default values
    Box (int w = 1, int l = 1, int h = 1): m_width(w), m_height(h), m_length(l){}
...
}

Konstruktory domyślne są jedną z specjalnych funkcji składowych. Jeśli konstruktory nie są deklarowane w klasie, kompilator udostępnia niejawny inline konstruktor domyślny.

#include <iostream>
using namespace std;

class Box {
public:
    int Volume() {return m_width * m_height * m_length;}
private:
    int m_width { 0 };
    int m_height { 0 };
    int m_length { 0 };
};

int main() {
    Box box1; // Invoke compiler-generated constructor
    cout << "box1.Volume: " << box1.Volume() << endl; // Outputs 0
}

Jeśli używasz niejawnego konstruktora domyślnego, zainicjuj elementy członkowskie w definicji klasy, jak pokazano w poprzednim przykładzie. Bez tych inicjatorów elementy członkowskie byłyby niezainicjowane, a wywołanie Volume() spowodowałoby wygenerowanie wartości bezużytecznej. Ogólnie rzecz biorąc, dobrym rozwiązaniem jest zainicjowanie elementów członkowskich w ten sposób nawet wtedy, gdy nie polega się na niejawnym konstruktorze domyślnym.

Kompilator może uniemożliwić generowanie niejawnego konstruktora domyślnego, definiując go jako usunięty:

    // Default constructor
    Box() = delete;

Domyślny konstruktor wygenerowany przez kompilator zostanie zdefiniowany jako usunięty, jeśli jakiekolwiek elementy członkowskie klasy nie są domyślnie skonstruowane. Na przykład wszystkie elementy członkowskie typu klasy i ich składowe typu klasy muszą mieć domyślny konstruktor i destruktory, które są dostępne. Wszystkie składowe danych typu odwołania i wszystkie const elementy członkowskie muszą mieć domyślny inicjator składowy.

Po wywołaniu konstruktora domyślnego wygenerowanego przez kompilator i próbie użycia nawiasów zostanie wyświetlone ostrzeżenie:

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

Ta instrukcja jest przykładem problemu "Most Vexing Parse". Można interpretować myclass md(); jako deklarację funkcji lub wywołanie domyślnego konstruktora. Ponieważ analizatory języka C++ faworyzują deklaracje innych rzeczy, wyrażenie jest traktowane jako deklaracja funkcji. Aby uzyskać więcej informacji, zobacz Większość analizy vexing.

Jeśli zadeklarowane są jakiekolwiek konstruktory inne niż domyślne, kompilator nie udostępnia konstruktora domyślnego:

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 box3; // C2512: no appropriate default constructor available
}

Jeśli klasa nie ma domyślnego konstruktora, nie można skonstruować tablicy obiektów tej klasy przy użyciu samej składni nawiasu kwadratowego. Na przykład, biorąc pod uwagę poprzedni blok kodu, tablica pól nie może być zadeklarowana w następujący sposób:

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

Można jednak użyć zestawu list inicjatora, aby zainicjować tablicę obiektów Box:

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

Aby uzyskać więcej informacji, zobacz Inicjatory.

Konstruktory kopii

Konstruktor kopiujący inicjuje obiekt, kopiując wartości składowe z obiektu tego samego typu. Jeśli wszystkie składowe klasy są prostymi typami, takimi jak wartości skalarne, konstruktor kopiujący wygenerowany przez kompilator jest wystarczający i nie musisz definiować własnych. Jeśli klasa wymaga bardziej złożonej inicjalizacji, należy zaimplementować niestandardowy konstruktor kopiowania. Jeśli na przykład element członkowski klasy jest wskaźnikiem, należy zdefiniować konstruktor kopiujący, aby przydzielić nową pamięć i skopiować wartości z obiektu wskazującego na inny obiekt. Konstruktor kopiowania wygenerowany przez kompilator po prostu kopiuje wskaźnik, dzięki czemu nowy wskaźnik nadal wskazuje lokalizację pamięci innego.

Konstruktor kopiujący może mieć jeden z następujących podpisów:

    Box(Box& other); // Avoid if possible--allows modification of other.
    Box(const Box& other);
    Box(volatile Box& other);
    Box(volatile const Box& other);

    // Additional parameters OK if they have default values
    Box(Box& other, int i = 42, string label = "Box");

Podczas definiowania konstruktora kopiowania należy również zdefiniować operator przypisania kopiowania (=). Aby uzyskać więcej informacji, zobacz Przypisywanie i kopiowanie konstruktorów i operatorów przypisania kopiowania.

Możesz uniemożliwić skopiowanie obiektu przez zdefiniowanie konstruktora kopiowania jako usuniętego:

    Box (const Box& other) = delete;

Próba skopiowania obiektu powoduje błąd C2280: próba odwołania się do usuniętej funkcji.

Przenoszenie konstruktorów

Konstruktor przenoszenia to specjalna funkcja składowa, która przenosi własność danych istniejącego obiektu do nowej zmiennej bez kopiowania oryginalnych danych. Przyjmuje odwołanie rvalue jako pierwszy parametr, a wszystkie późniejsze parametry muszą mieć wartości domyślne. Konstruktory przenoszenia mogą znacznie zwiększyć wydajność programu podczas przekazywania dużych obiektów.

Box(Box&& other);

Kompilator wybiera konstruktor przenoszenia, gdy obiekt jest inicjowany przez inny obiekt tego samego typu, jeśli drugi obiekt ma zostać zniszczony i nie potrzebuje już zasobów. W poniższym przykładzie pokazano jeden przypadek, gdy konstruktor przenoszenia jest wybierany przez rozpoznawanie przeciążenia. W konstruktorze, który wywołuje get_Box(), zwracana wartość jest wartością xvalue (eXpiring wartość). Nie jest ona przypisana do żadnej zmiennej i dlatego ma wyjść z zakresu. Aby zapewnić motywację dla tego przykładu, nadajmy usłudze Box duży wektor ciągów reprezentujący jego zawartość. Zamiast kopiować wektor i jego ciągi, konstruktor przenoszenia "kradnie" go z wygasającej wartości "box", aby wektor należy teraz do nowego obiektu. Wywołanie std::move metody to wszystko, co jest potrzebne, ponieważ obie stringvector klasy implementują własne konstruktory przenoszenia.

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

class Box {
public:
    Box() { std::cout << "default" << std::endl; }
    Box(int width, int height, int length)
       : m_width(width), m_height(height), m_length(length)
    {
        std::cout << "int,int,int" << std::endl;
    }
    Box(Box& other)
       : m_width(other.m_width), m_height(other.m_height), m_length(other.m_length)
    {
        std::cout << "copy" << std::endl;
    }
    Box(Box&& other) : m_width(other.m_width), m_height(other.m_height), m_length(other.m_length)
    {
        m_contents = std::move(other.m_contents);
        std::cout << "move" << std::endl;
    }
    int Volume() { return m_width * m_height * m_length; }
    void Add_Item(string item) { m_contents.push_back(item); }
    void Print_Contents()
    {
        for (const auto& item : m_contents)
        {
            cout << item << " ";
        }
    }
private:
    int m_width{ 0 };
    int m_height{ 0 };
    int m_length{ 0 };
    vector<string> m_contents;
};

Box get_Box()
{
    Box b(5, 10, 18); // "int,int,int"
    b.Add_Item("Toupee");
    b.Add_Item("Megaphone");
    b.Add_Item("Suit");

    return b;
}

int main()
{
    Box b; // "default"
    Box b1(b); // "copy"
    Box b2(get_Box()); // "move"
    cout << "b2 contents: ";
    b2.Print_Contents(); // Prove that we have all the values

    char ch;
    cin >> ch; // keep window open
    return 0;
}

Jeśli klasa nie definiuje konstruktora przenoszenia, kompilator generuje niejawny, jeśli nie ma konstruktora kopiowania zadeklarowanego przez użytkownika, operatora przypisania kopiowania, operatora przypisania przenoszenia lub destruktora. Jeśli nie zdefiniowano jawnego lub niejawnego konstruktora przenoszenia, operacje, które w przeciwnym razie używają konstruktora przenoszenia, zamiast tego używają konstruktora kopiowania. Jeśli klasa deklaruje konstruktor przenoszenia lub operator przypisania przenoszenia, niejawnie zadeklarowany konstruktor kopiowania jest definiowany jako usunięty.

Niejawnie zadeklarowany konstruktor przenoszenia jest definiowany jako usunięty, jeśli jakiekolwiek elementy członkowskie, które są typami klas, nie mają destruktora lub jeśli kompilator nie może określić, który konstruktor ma być używany do operacji przenoszenia.

Aby uzyskać więcej informacji na temat pisania konstruktora nietrywialnego przenoszenia, zobacz Move Constructors and Move Assignment Operators (C++)( Przenoszenie konstruktorów i operatorów przypisania przenoszenia (C++).

Jawnie domyślne i usunięte konstruktory

Można jawnie domyślnie kopiować konstruktory, konstruktory domyślne, konstruktory przenoszenia, operatory przypisania kopiowania, operatory przypisania przenoszenia i destruktory. Możesz jawnie usunąć wszystkie specjalne funkcje członkowskie.

class Box2
{
public:
    Box2() = delete;
    Box2(const Box2& other) = default;
    Box2& operator=(const Box2& other) = default;
    Box2(Box2&& other) = default;
    Box2& operator=(Box2&& other) = default;
    //...
};

Aby uzyskać więcej informacji, zobacz Jawnie domyślne i usunięte funkcje.

konstruktory constexpr

Konstruktor może być zadeklarowany jako constexpr , jeśli

  • jest zadeklarowana jako domyślna lub spełnia wszystkie warunki dla funkcji constexpr w ogóle;
  • klasa nie ma wirtualnych klas bazowych;
  • każdy z parametrów jest typem literału;
  • treść nie jest blokiem try-block funkcji;
  • inicjowane są wszystkie niestatyczne składowe danych i podobiekty klasy bazowej;
  • jeśli klasa to (a) związek mający składowe wariantu lub (b) ma anonimowe związki, inicjowany jest tylko jeden z członków unii;
  • każda niestatyczna składowa danych typu klasy, a wszystkie podobiekty klasy bazowej mają konstruktor constexpr

Konstruktory listy inicjatorów

Jeśli konstruktor przyjmuje std::initializer_list<T> jako parametr , a wszystkie inne parametry mają argumenty domyślne, konstruktor ten jest wybierany w rozpoznawaniu przeciążenia, gdy klasa jest tworzone przez inicjowanie bezpośrednie. Możesz użyć initializer_list, aby zainicjować dowolny element członkowski, który może go zaakceptować. Załóżmy na przykład, że klasa Box (pokazana wcześniej) ma składową std::vector<string>m_contents. Możesz podać konstruktor podobny do następującego:

    Box(initializer_list<string> list, int w = 0, int h = 0, int l = 0)
        : m_contents(list), m_width(w), m_height(h), m_length(l)
{}

Następnie utwórz obiekty Box w następujący sposób:

    Box b{ "apples", "oranges", "pears" }; // or ...
    Box b2(initializer_list<string> { "bread", "cheese", "wine" }, 2, 4, 6);

Konstruktory jawne

Jeśli klasa ma konstruktor z jednym parametrem lub jeśli wszystkie parametry z wyjątkiem jednego z nich mają wartość domyślną, typ parametru można niejawnie przekonwertować na typ klasy. Jeśli na przykład Box klasa ma konstruktor podobny do następującego:

Box(int size): m_width(size), m_length(size), m_height(size){}

Istnieje możliwość zainicjowania urządzenia Box w następujący sposób:

Box b = 42;

Możesz też przekazać int do funkcji, która przyjmuje usługę Box:

class ShippingOrder
{
public:
    ShippingOrder(Box b, double postage) : m_box(b), m_postage(postage){}

private:
    Box m_box;
    double m_postage;
}
//elsewhere...
    ShippingOrder so(42, 10.8);

Takie konwersje mogą być przydatne w niektórych przypadkach, ale częściej mogą prowadzić do subtelnych, ale poważnych błędów w kodzie. Ogólnie rzecz biorąc, należy użyć explicit słowa kluczowego w konstruktorze (i operatorach zdefiniowanych przez użytkownika), aby zapobiec tego rodzaju niejawnej konwersji typów:

explicit Box(int size): m_width(size), m_length(size), m_height(size){}

Gdy konstruktor jest jawny, ten wiersz powoduje błąd kompilatora: ShippingOrder so(42, 10.8);. Aby uzyskać więcej informacji, zobacz Konwersje typów zdefiniowanych przez użytkownika.

Kolejność budowy

Konstruktor wykonuje pracę w następującej kolejności:

  1. Wywołuje klasę podstawową i konstruktory składowych w kolejności deklaracji.

  2. Jeśli klasa jest pochodną wirtualnych klas bazowych, inicjuje wskaźniki wirtualnej bazy obiektu.

  3. Jeśli klasa ma lub dziedziczy funkcje wirtualne, inicjuje wskaźniki funkcji wirtualnych obiektu. Wskaźniki funkcji wirtualnych wskazują na tabelę funkcji wirtualnych tej klasy, aby umożliwić poprawne powiązanie wywołań funkcji wirtualnych z kodem.

  4. Wykonuje każdy kod w jego treści funkcji.

Poniższy przykład pokazuje kolejność, w której konstruktory klasy podstawowej i składowych są wywoływane w konstruktorze klasy pochodnej. Najpierw wywoływany jest konstruktor podstawowy. Następnie składowe klasy bazowej są inicjowane w kolejności, w której są wyświetlane w deklaracji klasy. Na koniec wywoływany jest konstruktor pochodny.

#include <iostream>

using namespace std;

class Contained1 {
public:
    Contained1() { cout << "Contained1 ctor\n"; }
};

class Contained2 {
public:
    Contained2() { cout << "Contained2 ctor\n"; }
};

class Contained3 {
public:
    Contained3() { cout << "Contained3 ctor\n"; }
};

class BaseContainer {
public:
    BaseContainer() { cout << "BaseContainer ctor\n"; }
private:
    Contained1 c1;
    Contained2 c2;
};

class DerivedContainer : public BaseContainer {
public:
    DerivedContainer() : BaseContainer() { cout << "DerivedContainer ctor\n"; }
private:
    Contained3 c3;
};

int main() {
    DerivedContainer dc;
}

Oto dane wyjściowe:

Contained1 ctor
Contained2 ctor
BaseContainer ctor
Contained3 ctor
DerivedContainer ctor

Konstruktor klasy pochodnej zawsze wywołuje konstruktora klasy bazowej, tak aby mógł polegać na całkowicie skonstruowanych klasach bazowych, zanim wszelkie dodatkowe prace zostaną wykonane. Konstruktory klasy bazowej są wywoływane w kolejności wyprowadzania — na przykład jeśli ClassA pochodzi z ClassBklasy , która pochodzi z ClassCklasy , ClassC konstruktor jest wywoływany najpierw, a następnie ClassB konstruktor, a następnie ClassA konstruktor.

Jeśli klasa bazowa nie ma konstruktora domyślnego, należy podać parametry konstruktora klasy bazowej w konstruktorze klasy pochodnej:

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);
}

Jeśli konstruktor zgłasza wyjątek, kolejność zniszczenia jest odwrotna do kolejności konstrukcji:

  1. Kod w treści funkcji konstruktora jest rozwinięty.

  2. Klasa podstawowa i obiekty składowe są usuwane w kolejności odwrotnej do deklarowania.

  3. Jeśli konstruktor nie deleguje, wszystkie w pełni skonstruowane obiekty i składowe klasy bazowej zostaną zniszczone. Jednak ponieważ sam obiekt nie jest w pełni skonstruowany, destruktor nie jest uruchamiany.

Konstruktory pochodne i rozszerzone inicjowanie agregacji

Jeśli konstruktor klasy bazowej nie jest publiczny, ale dostępny dla klasy pochodnej, nie można użyć pustych nawiasów klamrowych, aby zainicjować obiekt typu /std:c++17 pochodnego w trybie, a później w programie Visual Studio 2017 i nowszych wersjach.

W poniższym przykładzie pokazano zachowanie zgodne z językiem C++14:

struct Derived;

struct Base {
    friend struct Derived;
private:
    Base() {}
};

struct Derived : Base {};

Derived d1; // OK. No aggregate init involved.
Derived d2 {}; // OK in C++14: Calls Derived::Derived()
               // which can call Base ctor.

W języku C++17 Derived jest teraz uważany za typ agregacji. Oznacza to, że inicjowanie za pośrednictwem prywatnego konstruktora domyślnego Base odbywa się bezpośrednio w ramach rozszerzonej reguły inicjowania agregacji. Base Wcześniej konstruktor prywatny był wywoływany za pośrednictwem konstruktora Derived i zakończył się pomyślnie z powodu deklaracji friend .

W poniższym przykładzie pokazano zachowanie języka C++17 w programie Visual Studio 2017 lub nowszym w /std:c++17 trybie:

struct Derived;

struct Base {
    friend struct Derived;
private:
    Base() {}
};

struct Derived : Base {
    Derived() {} // add user-defined constructor
                 // to call with {} initialization
};

Derived d1; // OK. No aggregate init involved.

Derived d2 {}; // error C2248: 'Base::Base': can't access
               // private member declared in class 'Base'

Konstruktory dla klas, które mają wiele dziedziczenia

Jeśli klasa pochodzi z wielu klas bazowych, konstruktory klasy bazowej są wywoływane w kolejności, w której są wymienione w deklaracji klasy pochodnej:

#include <iostream>
using namespace std;

class BaseClass1 {
public:
    BaseClass1() { cout << "BaseClass1 ctor\n"; }
};
class BaseClass2 {
public:
    BaseClass2() { cout << "BaseClass2 ctor\n"; }
};
class BaseClass3 {
public:
    BaseClass3() { cout << "BaseClass3 ctor\n"; }
};
class DerivedClass : public BaseClass1,
                     public BaseClass2,
                     public BaseClass3
                     {
public:
    DerivedClass() { cout << "DerivedClass ctor\n"; }
};

int main() {
    DerivedClass dc;
}

Powinny się wyświetlić poniższe dane wyjściowe:

BaseClass1 ctor
BaseClass2 ctor
BaseClass3 ctor
DerivedClass ctor

Delegowanie konstruktorów

Delegowanie konstruktora wywołuje inny konstruktor w tej samej klasie, aby wykonać niektóre zadania inicjowania. Ta funkcja jest przydatna, gdy masz wiele konstruktorów, które muszą wykonywać podobną pracę. Główną logikę można napisać w jednym konstruktorze i wywołać z innych. W poniższym trywialnym przykładzie usługa Box(int) deleguje swoją pracę do usługi Box(int,int,int):

class Box {
public:
    // Default constructor
    Box() {}

    // Initialize a Box with equal dimensions (i.e. a cube)
    Box(int i) :  Box(i, i, i)  // delegating constructor
    {}

    // Initialize a Box with custom dimensions
    Box(int width, int length, int height)
        : m_width(width), m_length(length), m_height(height)
    {}
    //... rest of class as before
};

Obiekt utworzony przez konstruktory jest w pełni zainicjowany natychmiast po zakończeniu wszelkich konstruktorów. Aby uzyskać więcej informacji, zobacz Delegowanie konstruktorów.

Dziedziczenie konstruktorów (C++11)

Klasa pochodna może dziedziczyć konstruktory z bezpośredniej klasy bazowej przy użyciu using deklaracji, jak pokazano w poniższym przykładzie:

#include <iostream>
using namespace std;

class Base
{
public:
    Base() { cout << "Base()" << endl; }
    Base(const Base& other) { cout << "Base(Base&)" << endl; }
    explicit Base(int i) : num(i) { cout << "Base(int)" << endl; }
    explicit Base(char c) : letter(c) { cout << "Base(char)" << endl; }

private:
    int num;
    char letter;
};

class Derived : Base
{
public:
    // Inherit all constructors from Base
    using Base::Base;

private:
    // Can't initialize newMember from Base constructors.
    int newMember{ 0 };
};

int main()
{
    cout << "Derived d1(5) calls: ";
    Derived d1(5);
    cout << "Derived d1('c') calls: ";
    Derived d2('c');
    cout << "Derived d3 = d2 calls: " ;
    Derived d3 = d2;
    cout << "Derived d4 calls: ";
    Derived d4;
}

/* Output:
Derived d1(5) calls: Base(int)
Derived d1('c') calls: Base(char)
Derived d3 = d2 calls: Base(Base&)
Derived d4 calls: Base()*/

Program Visual Studio 2017 i nowsze: using instrukcja w /std:c++17 trybie i nowszym wprowadza zakres wszystkich konstruktorów z klasy bazowej, z wyjątkiem tych, które mają identyczny podpis konstruktorów w klasie pochodnej. Ogólnie rzecz biorąc, najlepiej używać konstruktorów dziedzicujących, gdy klasa pochodna deklaruje brak nowych składowych danych ani konstruktorów.

Szablon klasy może dziedziczyć wszystkie konstruktory z argumentu typu, jeśli ten typ określa klasę bazową:

template< typename T >
class Derived : T {
    using T::T;   // declare the constructors from T
    // ...
};

Klasa wyprowadzająca nie może dziedziczyć z wielu klas bazowych, jeśli te klasy bazowe mają konstruktory, które mają identyczny podpis.

Konstruktory i klasy złożone

Klasy zawierające składowe typu klasy są nazywane klasami złożonymi. Gdy jest tworzona składowa typu klasa klasy złożonej, konstruktor jest wywoływany przed konstruktorem tej klasy. Gdy klasa zamknięta nie ma domyślnego konstruktora, należy użyć listy inicjalizacji w konstruktorze klasy złożonej. W poprzednim StorageBox przykładzie, jeśli zmienisz typ m_label zmiennej składowej na nową Label klasę, musisz wywołać zarówno konstruktor klasy bazowej, jak i zainicjować m_label zmienną w konstruktorze StorageBox :

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"});
}

W tej sekcji

Zobacz też

Klasy i struktury