2009-09-07 14 views
5

Ho fatto il seguente piccolo programma: (fondamentalmente una classe che Couts se viene creato, copiato o distrutto e una principale che fa alcuni di che)std C++ elemento contenitore distruzione e comportamento inserimento

class Foo 
{ 
public: 
Foo(string name): _name(name) 
{ 
    cout << "Instance " << _name << " of Foo created!" << std::endl; 
}; 
Foo(const Foo& other): _name(other._name) 
{ 
    cout << "Instance " << _name << " of Foo copied!" << std::endl; 
}; 

~Foo() 
{ 
    cout << "Instance " << _name << " of Foo destroyed!" << std::endl; 
} 
string _name; 
}; 



int main(int argc, char**argv) 
{ 
Foo albert("Albert"); 
Foo bert("Bert"); 
{ 
    vector<Foo> v1, v2; 
    system("PAUSE"); 

    v1.push_back(albert); 
    system("PAUSE"); 

    v2.push_back(bert); 
    system("PAUSE"); 

    v1 = v2; 
    system("PAUSE"); 
} 
    system("PAUSE"); 
} 

L'output è il seguente:

Instance Albert of class Foo created! 
Instance Bert of class Foo created! 
Press any key... 
Instance Albert of class Foo copied!  
Instance Albert of class Foo copied!  // why another copy? 
Instance Albert of class Foo destroyed! // and destruction? 
Press any key... 
Instance Bert of class Foo copied! 
Instance Bert of class Foo copied! 
Instance Bert of class Foo destroyed! 
Press any key...      // v1=v2 why did the albert instance not get destroyed? 
Press any key...      
Instance Bert of class A destroyed! 
Instance Bert of class A destroyed! 
Press any key...      // there's still an albert living in the void 

Questo mi sembra molto strano. Perché mi preoccupo anche di passare qualcosa come riferimento se viene copiato due volte lo stesso? Perché v1.operator = (other) non distrugge gli elementi che contiene? Si adatterebbe bene con il comportamento di shared_ptr. Qualcuno può dirmi perché?

OLTRE Ho messo questo in un ciclo infinito e controllato l'Utilizzo memoria, non sembra produrre una perdita mem almeno.

OLTRE Ok, il mem non è un problema perché utilizza operatore = piuttosto che la copia ctor, ok grazie. Quando aggiungo

v1.reserve(10); 
v2.reserve(10); 

il numero logico di copie avviene. senza di ciò si rialloca e copia l'intero vettore per ogni singolo push_back, (che ritengo abbastanza ritardato anche per i vettori di piccole dimensioni). Guardando a questo mi prenderà in considerazione utilizzando Prenotate una di più e ottimizzare i miei operatori di assegnazione Col cavolo :)

AGGIUNTA: SINTESI

  1. Tutti questi problemi sembrano specifica a VC++ 2005.
  2. Se la dimensione dei due contenitori corrisponde, la mia implementazione utilizza l'operatore = sugli elementi invece di distruggere quelli vecchi e copiare quelli nuovi, che sembra pratica audio . SE le dimensioni differiscono, vengono utilizzate la normale distruzione e copia.
  3. Con l'implementazione del 2005, si deve usare la riserva! Altrimenti prestazione abissale e non standard.
  4. Queste scatole nere sono molto più nere di quanto pensassi.
+0

Hai provato a compilarlo come una build di rilascio? – jalf

+0

Sì. Stesso risultato – AndreasT

+0

È facile provarsi, basta copiare incollare in un progetto vuoto e, aggiungere include di iostream, vettore e stringa, e andare. – AndreasT

risposta

4

Perché mi preoccupo anche di passare qualcosa come riferimento se viene copiato due volte lo stesso?

È consigliabile considerare i tipi di contenitore STL come blackbox in grado di copiare gli oggetti archiviati tutte le volte che è necessario. Ad esempio, ogni volta che il contenitore viene ridimensionato, tutti gli oggetti verranno copiati.

È possibile che l'implementazione del push_back() del compilatore utilizzi una copia aggiuntiva temporanea. Sulla mia macchina (gcc su Mac OS X), non ci sono copie aggiuntive durante lo push_back() (in base all'output del programma).

Questa copia si trova da qualche parte nel codice STL, non nel costruttore di copia (poiché utilizza un riferimento).

Perché v1.operator = (altro) non distrugge gli elementi in esso contenuti?

Foo::operator= sarà chiamato per l'istanza "Albert" con l'istanza "bert" come argomento. Pertanto, non vi è alcuna operazione implicita di distruzione e copia qui. Si potrebbe desiderare di verificare questo, fornendo una propria implementazione per l'operatore:

Foo& operator=(const Foo& other) { 
    cout << "Instance " << other._name << " of Foo assigned to " << _name << "!" << std::endl; 
    return *this; 
} 

Questo produce il seguente output sulla mia macchina:

grado Alberto di Foo creato!
Istanza creata da Bert of Foo!
Istanza copiata da Albert of Foo!
Istanza Bert of Foo copiata!
Istanza Bert of Foo assegnata ad Albert!
Istanza Bert of Foo distrutta!
Istanza distrutta da Albert of Foo!
Istanza Bert of Foo distrutta!
Istanza distrutta da Albert of Foo!

+1

E tenete presente che il solo tenere traccia delle copie significa che il vostro copy ctor diventa più complesso al punto in cui l'ottimizzatore potrebbe non eliminarlo più. Quindi stai contando le chiamate di ctor che esistono solo perché sono contate! – MSalters

+0

L'argomento della scatola nera è valido, tuttavia, ho sempre sperato che queste non fossero stupide scatole nere :). Bene ancora un'altra delusione M $. – AndreasT

+0

@MSalters: questo è interessante. Il principio di indeterminazione del C++. – stribika

3

C'è un operatore generato automaticamente =. Quando si esegue v1 = v2 viene utilizzato quell'operatore. A quel punto una delle istanze "albert" diventa "bert". Prova ad aggiungere questa funzione per Foo:

Foo& operator = (const Foo& rval) { 
    cout << _name << " = " << rval._name << endl; 
    _name = rval._name; 
    return *this; 
} 

Questa è la stessa ha generato l'auto, ma stampa un messaggio di debug in modo da poter vedere cosa sta succedendo.

+0

bene e per quanto riguarda le copie in eccesso e la perdita di mem (che in realtà non sta accadendo, vedi sopra) – AndreasT

+1

Se si aggiunge questa funzione (o un contatore statico) si può vedere che non vi è alcuna perdita. Per quanto riguarda le copie extra, non vi è alcuna garanzia che un container STL copi le cose solo una volta. Può essere un passaggio per valore o qualche altra cosa a seconda dell'implementazione STL. – stribika

1

La "doppia copia" non si verifica quando è compilato con GCC. Questo deve essere specifico per il modo in cui std :: vector è implementato in VC++.

+0

Sembra specifico per VS2005 – AndreasT

1

Visual Studio 2008 mi dà il seguente risultato:

Instance Albert of Foo created! 
Instance Bert of Foo created! 
Press any key to continue . . . 
Instance Albert of Foo copied! 
Press any key to continue . . . 
Instance Bert of Foo copied! 
Press any key to continue . . . 
Press any key to continue . . . << here auto-generated operator= doing its job 
Instance Bert of Foo destroyed! 
Instance Bert of Foo destroyed! << this is Albert was originally 
Press any key to continue . . .

sembra essere che std::vector implementazione non è molto efficace in VS2005.

+1

Questo è sicuramente vero! – AndreasT

Problemi correlati