Condividi tramite


Costruttori (C++)

Per personalizzare la modalità di inizializzazione dei membri di una classe o per richiamare le funzioni quando viene creato un oggetto della classe, definire un costruttore. Ha nome uguale a quello della classe e non restituisce alcun valore. È possibile definire il numero di costruttori di overload necessari per personalizzare l'inizializzazione in vari modi. In genere, i costruttori hanno accessibilità pubblica in modo che il codice all'esterno della definizione della classe o della gerarchia di ereditarietà possa creare oggetti della classe. Ma è anche possibile dichiarare un costruttore come protected o private.

I costruttori possono facoltativamente accettare un elenco di inizializzatori membro. È un modo più efficiente per inizializzare i membri della classe rispetto all'assegnazione di valori nel corpo del costruttore. Nell'esempio seguente viene illustrata una classe Box con tre costruttori di overload. Gli ultimi due elenchi di elementi membro:

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

Quando si dichiara un'istanza di una classe, il compilatore sceglie il costruttore da richiamare in base alle regole di risoluzione dell'overload:

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)
}
  • I costruttori possono essere dichiarati come inline, explicit, friendo constexpr.
  • Un costruttore può inizializzare un oggetto dichiarato come consto volatile const volatile. L'oggetto diventa const dopo il completamento del costruttore.
  • Per definire un costruttore in un file di implementazione, assegnare un nome completo come qualsiasi altra funzione membro: Box::Box(){...}.

Elenchi di inizializzatori di membri

Un costruttore può facoltativamente avere un elenco di inizializzatori membro, che inizializza i membri della classe prima dell'esecuzione del corpo del costruttore. Un elenco di inizializzatori di membri non corrisponde a un elenco di inizializzatori di tipo std::initializer_list<T>.

Preferisce gli elenchi di inizializzatori membro rispetto all'assegnazione di valori nel corpo del costruttore. Un elenco di inizializzatori di membri inizializza direttamente i membri. L'esempio seguente mostra l'elenco di inizializzatori membro, costituito da tutte le identifier(argument) espressioni dopo i due punti:

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

L'identificatore deve fare riferimento a un membro della classe; viene inizializzato con il valore dell'argomento. L'argomento può essere uno dei parametri del costruttore, una chiamata di funzione o .std::initializer_list<T>

const i membri e i membri di tipo riferimento devono essere inizializzati nell'elenco di inizializzatori membri.

Per garantire che le classi di base siano completamente inizializzate prima dell'esecuzione del costruttore derivato, chiamare qualsiasi costruttore della classe di base con parametri nell'elenco di inizializzatori.

Costruttori predefiniti

I costruttori predefiniti in genere non hanno parametri, ma possono avere parametri con valori predefiniti.

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){}
...
}

I costruttori predefiniti sono una delle funzioni membro speciali. Se non viene dichiarato alcun costruttore in una classe, il compilatore fornisce un costruttore predefinito implicito inline .

#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
}

Se si fa affidamento su un costruttore predefinito implicito, assicurarsi di inizializzare i membri nella definizione della classe, come illustrato nell'esempio precedente. Senza tali inizializzatori, i membri non verranno inizializzati e la chiamata a Volume() produrrebbe un valore di Garbage. In generale, è consigliabile inizializzare i membri in questo modo anche quando non si fa affidamento su un costruttore predefinito implicito.

È possibile impedire al compilatore di generare un costruttore predefinito implicito definendolo come eliminato:

    // Default constructor
    Box() = delete;

Un costruttore predefinito generato dal compilatore verrà definito come eliminato se i membri di una classe non sono costruttibili per impostazione predefinita. Ad esempio, tutti i membri del tipo di classe e i relativi membri di tipo classe devono avere un costruttore e distruttori predefiniti accessibili. Tutti i membri dati di tipo riferimento e tutti i const membri devono avere un inizializzatore di membro predefinito.

Quando si chiama un costruttore predefinito generato dal compilatore e si tenta di usare le parentesi, viene generato un avviso:

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

Questa istruzione è un esempio del problema "Most Vexing Parse". È possibile interpretare myclass md(); come dichiarazione di funzione o come chiamata di un costruttore predefinito. Poiché i parser C++ favoriscono le dichiarazioni rispetto ad altre cose, l'espressione viene considerata come una dichiarazione di funzione. Per altre informazioni, vedere La maggior parte dell'analisi vexing.

Se vengono dichiarati costruttori non predefiniti, il compilatore non fornisce un costruttore predefinito:

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
}

Se una classe non ha un costruttore predefinito, non è possibile costruire una matrice di oggetti di tale classe usando solo la sintassi tra parentesi quadre. Ad esempio, dato il blocco di codice precedente, non è possibile dichiarare una matrice di caselle come segue:

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

Tuttavia, è possibile usare un set di elenchi di inizializzatori per inizializzare una matrice di oggetti Box:

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

Per altre informazioni, vedere Inizializzatori.

Costruttori di copia

Un costruttore di copia inizializza un oggetto copiando i valori del membro da un oggetto dello stesso tipo. Se i membri della classe sono tutti tipi semplici, ad esempio valori scalari, il costruttore di copia generato dal compilatore è sufficiente e non è necessario definire il proprio. Se la classe richiede un'inizializzazione più complessa, è necessario implementare un costruttore di copia personalizzato. Ad esempio, se un membro di classe è un puntatore, è necessario definire un costruttore di copia per allocare nuova memoria e copiare i valori dall'oggetto a cui punta l'altro. Il costruttore di copia generato dal compilatore copia semplicemente il puntatore, in modo che il nuovo puntatore punti comunque alla posizione di memoria dell'altro.

Un costruttore di copia può avere una di queste firme:

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

Quando si definisce un costruttore di copia, è necessario definire anche un operatore di assegnazione di copia (=). Per altre informazioni, vedere Costruttore di assegnazione e copia e operatori di assegnazione di copia.

È possibile impedire che l'oggetto venga copiato definendo il costruttore di copia come eliminato:

    Box (const Box& other) = delete;

Il tentativo di copiare l'oggetto genera l'errore C2280: tentativo di fare riferimento a una funzione eliminata.

Costruttore di spostamento

Un costruttore di spostamento è una funzione membro speciale che sposta la proprietà dei dati di un oggetto esistente in una nuova variabile senza copiare i dati originali. Accetta un riferimento rvalue come primo parametro e i parametri successivi devono avere valori predefiniti. I costruttori di spostamento possono aumentare significativamente l'efficienza del programma quando si passano oggetti di grandi dimensioni.

Box(Box&& other);

Il compilatore sceglie un costruttore di spostamento quando l'oggetto viene inizializzato da un altro oggetto dello stesso tipo, se l'altro oggetto sta per essere eliminato definitivamente e non richiede più le relative risorse. Nell'esempio seguente viene illustrato un caso in cui un costruttore di spostamento viene selezionato dalla risoluzione dell'overload. Nel costruttore che chiama get_Box(), il valore restituito è un valore xvalue (valore eXpiring). Non è assegnato ad alcuna variabile e quindi sta per uscire dall'ambito. Per fornire la motivazione per questo esempio, diamo a Box un vettore di stringhe di grandi dimensioni che ne rappresentano il contenuto. Invece di copiare il vettore e le relative stringhe, il costruttore di spostamento lo "ruba" dal valore "box" in scadenza in modo che il vettore appartenga ora al nuovo oggetto. La chiamata a std::move è tutto ciò che è necessario perché entrambe vector le classi e string implementano i propri costruttori di spostamento.

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

Se una classe non definisce un costruttore di spostamento, il compilatore genera uno implicito se non esiste un costruttore di copia dichiarato dall'utente, un operatore di assegnazione di copia, un operatore di assegnazione di spostamento o un distruttore. Se non viene definito alcun costruttore di spostamento esplicito o implicito, le operazioni che altrimenti usano un costruttore di spostamento usano il costruttore di copia. Se una classe dichiara un costruttore di spostamento o un operatore di assegnazione di spostamento, il costruttore di copia dichiarato in modo implicito viene definito come eliminato.

Un costruttore di spostamento dichiarato in modo implicito viene definito come eliminato se i membri che sono tipi di classe non dispongono di un distruttore o se il compilatore non è in grado di determinare quale costruttore usare per l'operazione di spostamento.

Per altre informazioni su come scrivere un costruttore di spostamento non semplice, vedere Move Constructors and Move Assignment Operators (C++).

Costruttori predefiniti ed eliminati in modo esplicito

È possibile impostare in modo esplicito costruttori di copia predefiniti , costruttori predefiniti, costruttori di spostamento, operatori di assegnazione di copia, operatori di assegnazione di spostamento e distruttori. È possibile eliminare in modo esplicito tutte le funzioni membro speciali.

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;
    //...
};

Per altre informazioni, vedere Funzioni predefinite ed eliminate in modo esplicito.

costruttori constexpr

Un costruttore può essere dichiarato come constexpr se

  • è dichiarato come predefinito oppure soddisfa tutte le condizioni per le funzioni constexpr in generale;
  • la classe non dispone di classi di base virtuali;
  • ognuno dei parametri è un tipo letterale;
  • il corpo non è una funzione try-block;
  • tutti i membri dati non statici e i sottooggetti della classe base vengono inizializzati;
  • se la classe è (a) un'unione con membri varianti o (b) dispone di unioni anonime, viene inizializzato solo uno dei membri dell'unione;
  • ogni membro dati non statico del tipo di classe e tutti gli oggetti secondari della classe base hanno un costruttore constexpr

Costruttori dell'elenco di inizializzatori

Se un costruttore accetta come std::initializer_list<T> parametro e tutti gli altri parametri hanno argomenti predefiniti, tale costruttore viene selezionato nella risoluzione dell'overload quando viene creata un'istanza della classe tramite l'inizializzazione diretta. È possibile utilizzare il initializer_list per inizializzare qualsiasi membro che possa accettarlo. Si supponga, ad esempio, che la classe Box (illustrata in precedenza) abbia un std::vector<string> membro m_contents. È possibile fornire un costruttore simile al seguente:

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

Creare quindi oggetti Box come segue:

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

Costruttori espliciti

Se una classe ha un costruttore con un singolo parametro o se tutti i parametri tranne uno hanno un valore predefinito, sarà possibile convertire implicitamente il tipo di parametro nel tipo di classe. Ad esempio, se la classe Box ha un costruttore analogo al seguente:

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

È possibile inizializzare un box simile al seguente:

Box b = 42;

Oppure passare un valore int a una funzione che accetta 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);

Queste conversioni possono risultare utili in alcuni casi, ma provocano più spesso errori poco evidenti ma gravi nel codice. Come regola generale, è consigliabile usare la explicit parola chiave in un costruttore (e operatori definiti dall'utente) per impedire questo tipo di conversione implicita dei tipi:

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

Quando il costruttore è esplicito, questa riga provoca un errore del compilatore: ShippingOrder so(42, 10.8);. Per altre informazioni, vedere Conversioni di tipi definite dall'utente.

Ordine di costruzione

Di seguito è indicato l'ordine in base al quale un costruttore esegue le attività.

  1. Chiama i costruttori delle classi base e dei membri nell'ordine di dichiarazione.

  2. Se la classe è derivata dalle classi base virtuali, inizializza i puntatori di base virtuali dell'oggetto.

  3. Se la classe dispone di funzioni virtuali o le eredita, inizializza i puntatori a funzioni virtuali dell'oggetto. I puntatori a funzioni virtuali puntano alla tabella delle funzioni virtuali della classe per consentire la corretta associazione delle chiamate di funzioni virtuali al codice.

  4. Esegue il codice nel corpo della funzione.

Nell'esempio seguente viene illustrato l'ordine in cui vengono chiamati i costruttori di classi base e di membri in un costruttore per una classe derivata. In primo luogo, viene chiamato il costruttore di base. I membri della classe base vengono quindi inizializzati nell'ordine in cui vengono visualizzati nella dichiarazione di classe. Viene infine chiamato il costruttore derivato.

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

Di seguito è riportato l'output generato:

Contained1 ctor
Contained2 ctor
BaseContainer ctor
Contained3 ctor
DerivedContainer ctor

Un costruttore di classi derivate chiama sempre un costruttore di classi base, in modo da poter utilizzare le classi base completamente costruite prima che vengano completate eventuali operazioni aggiuntive. I costruttori della classe base vengono chiamati in ordine di derivazione, ad esempio se ClassA è derivato da ClassB, derivato da ClassC, il costruttore viene chiamato prima, quindi ClassC il ClassB costruttore, quindi il ClassA costruttore.

Se una classe base non dispone di un costruttore predefinito, è necessario fornire i parametri del costruttore della classe base nel costruttore della classe derivata:

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

Se un costruttore genera un'eccezione, l'ordine di eliminazione permanente è l'inverso dell'ordine di costruzione:

  1. Il codice nel corpo della funzione del costruttore viene rimosso.

  2. Gli oggetti classe base e membro vengono eliminati definitivamente nell'ordine inverso rispetto alla dichiarazione.

  3. Se il costruttore non delega, tutti gli oggetti e i membri della classe base completamente costruiti vengono eliminati definitivamente. Tuttavia, poiché l'oggetto stesso non è completamente costruito, il distruttore non viene eseguito.

Costruttori derivati e inizializzazione estesa dell'aggregazione

Se il costruttore di una classe base non è pubblico, ma accessibile a una classe derivata, non è possibile usare parentesi graffe vuote per inizializzare un oggetto del tipo derivato in /std:c++17 modalità e versioni successive in Visual Studio 2017 e versioni successive.

L'esempio seguente illustra il comportamento conforme di 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.

In C++17, Derived è ora considerato un tipo di aggregazione. Di conseguenza, l'inizializzazione di Base tramite il costruttore predefinito privato si verifica direttamente come parte della regola di inizializzazione delle aggregazioni estesa. In precedenza, il Base costruttore privato è stato chiamato tramite il Derived costruttore e ha avuto esito positivo a causa della friend dichiarazione.

L'esempio seguente illustra il comportamento di C++17 in Visual Studio 2017 e versioni successive in /std:c++17 modalità:

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'

Costruttori per classi con più ereditarietà

Se una classe è derivata da più classi di base, i costruttori della classe base vengono richiamati nell'ordine in cui sono elencati nella dichiarazione della classe derivata:

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

È necessario prevedere il seguente output:

BaseClass1 ctor
BaseClass2 ctor
BaseClass3 ctor
DerivedClass ctor

Delega dei costruttori

Un costruttore di delega chiama un costruttore diverso nella stessa classe per eseguire alcune operazioni di inizializzazione. Questa funzionalità è utile quando sono presenti più costruttori che devono eseguire operazioni simili. È possibile scrivere la logica principale in un costruttore e richiamarla da altri. Nell'esempio semplice seguente Box(int) delega il proprio lavoro a 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
};

L'oggetto creato dai costruttori viene completamente inizializzato non appena viene completato un costruttore. Per altre informazioni, vedere Delega dei costruttori.

Ereditarietà dei costruttori (C++11)

Una classe derivata può ereditare i costruttori da una classe base diretta usando una using dichiarazione come illustrato nell'esempio seguente:

#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()*/

Visual Studio 2017 e versioni successive: l'istruzione using in /std:c++17 modalità e successiva introduce nell'ambito tutti i costruttori della classe base, ad eccezione di quelli con una firma identica ai costruttori nella classe derivata. In generale, è consigliabile usare costruttori che ereditano quando la classe derivata non dichiara nuovi membri dati o costruttori.

Un modello di classe può ereditare tutti i costruttori da un argomento di tipo, se tale tipo specifica una classe base:

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

Una classe di derivazione non può ereditare da più classi di base se tali classi di base hanno costruttori con una firma identica.

Costruttori e classi composite

Le classi che contengono membri di tipo classe sono note come classi composite. Quando viene creato un membro di tipo classe di una classe composita, il costruttore viene chiamato prima del costruttore della classe. Quando una classe contenuta è priva di un costruttore predefinito, è necessario utilizzare un elenco di inizializzazione nel costruttore della classe composita. Nell'esempio precedente relativo a StorageBox, se si modifica il tipo della variabile membro m_label in una nuova classe Label, è necessario chiamare il costruttore della classe base e inizializzare la variabile m_label nel costruttore 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"});
}

In questa sezione

Vedi anche

Classi e struct