Domanda

So che il compilatore a volte fornisce un costruttore di copie predefinito se non ti implementi. Sono confuso su ciò che fa esattamente questo costruttore. Se ho una classe che contiene altri oggetti, nessuno dei quali ha un costruttore di copie dichiarato, quale sarà il comportamento? Ad esempio, una classe come questa:

class Foo {
  Bar bar;
};

class Bar {
  int i;
  Baz baz;
};

class Baz {
  int j;
};

Ora, se lo faccio:

Foo f1;
Foo f2(f1);

Cosa farà il costruttore di copie predefinito? Il costruttore di copie generato dal compilatore in Foo chiamerà il costruttore generato dal compilatore in Bar per copiare su bar , che chiamerà quindi il compilatore- costruttore di copie generato in Baz ?

È stato utile?

Soluzione

Foo f1;
Foo f2(f1);

Sì, questo farà ciò che ti aspetti:
Viene chiamato il costruttore di copie f2 Foo :: Foo (Foo const & amp;).
Questa copia costruisce la sua classe base e quindi ogni membro (ricorsivamente)

Se definisci una classe come questa:

class X: public Y
{
    private:
        int     m_a;
        char*   m_b;
        Z       m_c;
};

I seguenti metodi saranno definiti dal tuo compilatore.

  • Funzione di costruzione (impostazione predefinita) (2 versioni)
  • Funzione di costruzione (copia)
  • Distruttore (predefinito)
  • Operatore di assegnazione

Funzione di costruzione: impostazione predefinita:

In realtà ci sono due costruttori predefiniti.
Uno è usato per zero-inizializzazione mentre l'altro è usato per valore-inizializzazione . L'usato dipende dal fatto che si usi () durante l'inizializzazione o meno.

// Zero-Initialization compiler generated constructor
X::X()
    :Y()                // Calls the base constructor
                        //     If this is compiler generated use 
                        //     the `Zero-Initialization version'
    ,m_a(0)             // Default construction of basic PODS zeros them
    ,m_b(0)             // 
    m_c()               // Calls the default constructor of Z
                        //     If this is compiler generated use 
                        //     the `Zero-Initialization version'
{
}

// Value-Initialization compiler generated constructor
X::X()
    :Y()                // Calls the base constructor
                        //     If this is compiler generated use 
                        //     the `Value-Initialization version'
    //,m_a()            // Default construction of basic PODS does nothing
    //,m_b()            // The values are un-initialized.
    m_c()               // Calls the default constructor of Z
                        //     If this is compiler generated use 
                        //     the `Value-Initialization version'
{
}

Note: se la classe di base o qualsiasi membro non ha un costruttore predefinito visibile valido, non è possibile generare il costruttore predefinito. Questo non è un errore a meno che il codice non tenti di utilizzare il costruttore predefinito (quindi solo un errore di compilazione).

Funzione di costruzione (copia)

X::X(X const& copy)
    :Y(copy)            // Calls the base copy constructor
    ,m_a(copy.m_a)      // Calls each members copy constructor
    ,m_b(copy.m_b)
    ,m_c(copy.m_c)
{}

Note: se la classe di base o qualsiasi membro non dispone di un costruttore di copie visibile valido, non è possibile generare il costruttore di copie. Questo non è un errore a meno che il codice non tenti di utilizzare il costruttore della copia (quindi solo un errore di compilazione).

Operatore di assegnazione

X& operator=(X const& copy)
{
    Y::operator=(copy); // Calls the base assignment operator
    m_a = copy.m_a;     // Calls each members assignment operator
    m_b = copy.m_b;
    m_c = copy.m_c;

    return *this;
}

Note: se la classe base o altri membri non dispongono di un operatore di assegnazione valido valido, non è possibile generare l'operatore di assegnazione. Questo non è un errore a meno che il codice non tenti di utilizzare l'operatore di assegnazione (quindi solo un errore di compilazione).

Destructor

X::~X()
{
                        // First runs the destructor code
}
    // This is psudo code.
    // But the equiv of this code happens in every destructor
    m_c.~Z();           // Calls the destructor for each member
    // m_b              // PODs and pointers destructors do nothing
    // m_a          
    ~Y();               // Call the base class destructor
  • Se viene dichiarato qualsiasi costruttore (inclusa la copia), il costruttore predefinito non viene implementato dal compilatore.
  • Se viene dichiarato il costruttore della copia, il compilatore non ne genererà uno.
  • Se viene dichiarato l'operatore di assegnazione, il compilatore non ne genererà uno.
  • Se viene dichiarato un distruttore, il compilatore non ne genererà uno.

Guardando il tuo codice vengono generati i seguenti costruttori di copie:

Foo::Foo(Foo const& copy)
    :bar(copy.bar)
{}

Bar::Bar(Bar const& copy)
    :i(copy.i)
    ,baz(copy.baz)
{}

Baz::Baz(Baz const& copy)
    :j(copy.j)
{}

Altri suggerimenti

Il compilatore fornisce un costruttore di copie a meno che dichiari (nota: non definisci ) tu stesso. Il costruttore di copie generato dal compilatore chiama semplicemente il costruttore di copie di ciascun membro della classe (e di ciascuna classe di base).

Lo stesso vale per l'operatore di assegnazione e il distruttore, BTW. È diverso per il costruttore predefinito, tuttavia: viene fornito dal compilatore solo se non si dichiara alcun altro costruttore.

Sì, il costruttore di copie generato dal compilatore esegue una copia in base ai membri, nell'ordine in cui i membri vengono dichiarati nella classe contenitore. Se uno qualsiasi dei tipi di membri non offre essi stessi un costruttore di copie, non è possibile generare il potenziale costruttore di copie della classe contenente. Potrebbe essere ancora possibile scriverne uno manualmente, se si può decidere su alcuni mezzi appropriati per inizializzare il valore del membro che non può essere costruito-copia - forse usando uno dei suoi altri costruttori.

Il C ++ costruttore di copie predefinito crea un copia superficiale . Una copia superficiale non creerà nuove copie di oggetti a cui l'oggetto originale può fare riferimento; gli oggetti vecchi e nuovi conterranno semplicemente puntatori distinti nella stessa posizione di memoria.

Il compilatore genererà i costruttori necessari per te.

Tuttavia, non appena definisci tu stesso un costruttore di copia, il compilatore rinuncia a generare qualsiasi cosa per quella classe e genererà ed errore se non hai definito i costruttori appropriati.

Usando il tuo esempio:

class Baz {
    Baz(const Baz& b) {}
    int j;
};
class Bar {
    int i;
    Baz baz;
};
class Foo {
    Bar bar;
};

Cercando di creare un'istanza predefinita o di costruire una copia Foo genererà un errore poiché Baz non è costruibile per la copia e il compilatore non può generare il default e copiare il costroctor per Foo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top