6

considerare questo pezzo di codice:costruttore di copia delle classi multiply-ereditate

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

class Base 
{ 
    char _type; 
public: 
    Base(char type): 
     _type(type) 
    {} 

    ~Base() { 
     cout << "Base destructor: " << _type << endl; 
    } 
}; 

class uncopyable 
{ 
    protected: 
     uncopyable() {} 
     ~uncopyable() {} 
    private: 
     uncopyable(const uncopyable&); 
     const uncopyable& operator=(const uncopyable&); 
}; 

class Child : public Base, private uncopyable 
{ 
    int j; 
public: 
    Child(): 
     Base('c') 
    {} 
    ~Child() { 
     cout << "Child destructor" << endl; 
    } 
}; 


int main() 
{ 
    vector<Base> v; 
    Base b('b'); 
    Child c; 

    v.push_back(b); 
    v.push_back(c); 
    return 0; 
} 

L'uscita sul mio sistema è:

Base destructor: b 
Child destructor 
Base destructor: c 
Base destructor: b 
Base destructor: b 
Base destructor: c 

Le mie domande sono:

  • Perché è il distruttore di Base (con tipo b) chiamato tre volte anziché due (abbiamo più di due copie dell'oggetto b)?

  • Cosa succede quando si copia un oggetto di tipo Child, considerando che il costruttore di copia di uno dei suoi genitori è privato. È un comportamento indefinito?

  • Mi aspettavo di ottenere un errore in fase di compilazione ogni volta che provo a copiare un oggetto di tipo Child. Pensavo che il costruttore di copia predefinito del figlio avrebbe provato a chiamare il costruttore di copie privato della classe Uncopyable e causare errori di compilazione. Perché non fornisce errori di compilazione?

Il motivo per cui il codice è stato progettato in questo modo è perché la classe Child è enorme.

Il comportamento desiderato sta lanciando i dati figlio ogni volta che un client tenta di copiare un oggetto Child (chiamando il distruttore di Child senza chiamare il distruttore di Base).

Questo pezzo di codice lo raggiunge, ma suppongo che si traduca in un comportamento indefinito e abbia una perdita di memoria (non chiama mai il distruttore di Child per l'istanza copiata).

+0

Vuoi un errore 'compile-time' o vuoi buttare via i dati di Child? Queste sono affermazioni contrastanti, a meno che mi manchi qualcosa. – Chip

+0

@Chip Risolto il problema. Grazie. –

risposta

9

Ecco cosa accade nel codice:

int main() 
{ 
    vector<Base> v; // 1 
    Base b('b');  // 2 
    Child c;   // 3 

    v.push_back(b); // 4 
    v.push_back(c); // 5 
    return 0; 
}      // 6 
  1. linea 1: vettore v costruito

  2. linea 2: Base b costruito (chiamando il costruttore di Base)

  3. linea 3: Child c costruito (chiamando il costruttore di Child e il costruttore di Base)

  4. riga 4: v è corrente alla massima capacità e deve essere ridimensionata.
    La memoria è allocata per 1 elemento di Base di v.
    Base b copiata in v [0] (chiamando il costruttore di copie di Base).

  5. riga 5: v è di nuovo alla massima capacità e deve essere ridimensionato.
    La memoria è allocata per 2 elementi di Base di v.
    La vecchia v [0] viene copiata nella nuova v [0] (chiamando il costruttore di copie di Base).
    La vecchia v [0] viene cancellata (chiamando il distruttore di Base ("Base destructor: b")).
    Il bambino c viene copiato in v [1] (chiamando il costruttore di copie di Base).

  6. riga 6: c, b e v sono fuori portata.
    Il bambino c viene eliminato (chiamando il distruttore del bambino ("Distruttore figlio") quindi il distruttore di Base ("Base destructor: c").
    Base b viene eliminata (chiamando il distruttore di Base ("Base destructor: b")).
    Base v [0], v [1] sono cancellati (chiamando il distruttore di Base due volte ("Base destructor: b", "Base destructor: c")).

Non c'è perdita di memoria: per ogni costruttore nella sequenza precedente viene chiamato un distruttore corrispondente.

Inoltre, sembra che tu sia molto confuso riguardo ai costruttori di copie. Il child c viene passato a push_back come Base & - che quindi chiama il costruttore di copie di Base come previsto. Poiché il costruttore implicito di copia di Base non è virtuale o sovrascritto, avere Child derivato da uncopyable non fa nulla per cambiare questo.

Si noti che a vector<Base> non è possibile memorizzare un oggetto di tipo Bambino; sa solo allocare abbastanza memoria per una Base. Ciò che accade quando si assegna un'istanza di Child a una Base è noto come slicing, che, anche se spesso non intenzionale e frainteso, sembra che potrebbe effettivamente essere ciò che si vuole nello scenario descritto.

+0

Grazie. Ho trovato anche questo utile: http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c –

0

MODIFICA: La risposta è errata .. attualmente si modifica in una risposta migliore.

Perché il distruttore di Base (con tipo b) chiamato tre volte anziché due (abbiamo più di due copie dell'oggetto b)?

Molto probabilmente il Vector sta facendo una copia di b. I vettori lo fanno spesso.

Cosa succede quando si copia un oggetto di tipo Bambino, considerando che il costruttore di copia di uno dei suoi genitori è privato. È un comportamento indefinito?

No. Il costruttore di copie di C chiamerà il costruttore di copie delle classi di base. Quindi, se i costruttori di copia della classe base sono privati, non verranno compilati.

Ho bisogno di ottenere un errore in fase di compilazione ogni volta che provo a copiare un oggetto di tipo Child mentre, permettendo la copia di oggetti di classe Base. Qual è il modo migliore per farlo?

Dichiarare un costruttore di copia privata per il bambino in questo modo:

private: 
    Child(const Child& a) { 
     throw "cannot make a copy"; 
    } 

il comportamento desiderato è buttare via i dati del bambino ogni volta che un client tenta di copiare un oggetto figlio (chiamando il distruttore del bambino senza chiamare il distruttore di Base).

Non so cosa intendi. Il costruttore di copie significa creare un nuovo oggetto. Non è possibile eseguire operazioni sull'oggetto (vecchio).

+0

1. Il vettore IMHO dovrebbe copiarlo una volta. Non due volte 2. IMHO il costruttore implicito di copia del figlio dovrebbe chiamare il costruttore di copia del/i genitore/i. http://stackoverflow.com/questions/9178204/why-does-the-implicit-copy-constructor-calls-the-base-class-copy-constructor-and 3. Non funziona (puoi provarlo). Poiché l'oggetto viene prima convertito per tipo su genitore e quindi copiato. –

+0

Nel codice suggerito, stiamo creando un nuovo figlio e non copiamo i dati. Sta sconfiggendo lo scopo di non creare di nuovo un nuovo oggetto. –

+0

Penso che mi manchi qualcosa. Il costruttore di copie verrà chiamato quando si sta creando un oggetto da un altro oggetto. Questo è ciò che fa un costruttore di copia ... Puoi spiegare cosa vuoi fare? – Chip

3

Mi aspettavo di ottenere un errore in fase di compilazione ogni volta che provo a copiare un oggetto di tipo Child.

Non si sta copiando un oggetto Child. Quando si inserisce Childc in un vector<Base>, viene copiato solo lo Base. È praticamente lo stesso dell'esecuzione di b = c;. Se si copia/assegna Child si otterrà un errore.

Child d = c; // compile error 

il costruttore di copia di default chiamerà i costruttori di copia di eventuali oggetti di classe base e membri e fare una copia bit per bit per i primitivi e puntatori.

+0

Grazie. Questo sembra corretto. Che mi dici della chiamata in più al distruttore di Base? –

+1

@Shayan, è presumibilmente un vettore di copia extra realizzato durante la riallocazione del suo archivio. Il vettore differente <> impls può comportarsi diversamente se sovra allocano. – xan

Problemi correlati