2012-11-14 15 views
23

Ecco il codice che produce output diversi in g ++ 4.7 e vs2012 (cl17).C++: copia per valore in funzione i parametri producono due oggetti in vs2012

#include <iostream> 

using namespace std; 

class A 
{ 
public: 
    A() { cout << "1" << endl; } 
    ~A() { cout << "2" << endl; } 
}; 

class B : public A 
{ 
public: 
    B() { cout << "3" << endl; } 
    ~B() { cout << "4" << endl; } 
}; 

void func(A a) {} 

int main() 
{ 
    B b; 
    func(b); 
    return 0; 
} 

L'uscita GCC è 13242, mentre le uscite cl 132242.

Perché il compilatore cl produce un secondo oggetto A mentre esegue una copia nello stack e per quale scopo?

+2

testato su VS2010, il risultato è "132.242" – Apokal

+1

Clang 4.1 produce "13242" –

+4

versione VS produrre solo 13.242 ma non versione di debug – billz

risposta

-3

Si è verificato un errore del compilatore.

La funzionalità corretta è spiegato qui di seguito:


La funzione func ha bisogno di creare una copia dell'oggetto (ma attenzione per le operazioni di affettamento).

Quindi, quello che succede è questo:

int main() 
{ 
    // create object B, which first creates the base object A 
    B b; 
    // create object A, using this copy constructor : A(const B&) 
    func(b); 
} 

La chiamata in più ~ A() è realizzato quando l'oggetto copia-costruito A viene distrutta alla fine della chiamata func.

+5

In "cl output" ci sono due chiamate a '~ A' alla fine di' func() '. Questa sembra essere la domanda. –

+0

@KevinBallard Ho spiegato cosa dovrebbe accadere. Chiamerei il comportamento un bug del compilatore. Alcuni lo chiamerebbero un'estensione;) –

+2

@ BЈовић Quello che dovrebbe succedere è già nella domanda; questo è solo un ripensamento. – Gorpik

5

Sembra un bug del compilatore.
Lo standard C++ non utilizza il termine Oggetto Soggetto, Si passa un oggetto del tipo B a una funzione che riceve un parametro del tipo A. Il compilatore applicherà la solita risoluzione di sovraccarico per trovare la corrispondenza appropriata. In questo caso:
La classe Base A ha il costruttore di copie fornito dal compilatore, che prenderà un riferimento a A e in assenza di altre funzioni di conversione è la corrispondenza migliore e dovrebbe essere utilizzata dal compilatore.

Si noti che se fosse disponibile una conversione migliore, sarebbe utilizzata. Ad esempio: se A aveva un costruttore A::A(B const&), oltre al costruttore di copie, sarebbe utilizzato questo costruttore, anziché il costruttore di copie.

+4

Un costruttore di copia banale per 'A' risolve il problema in VS2010, che per me non ha molto senso. – Gorpik

+1

@Gorpik: Più motivi per ritenere che sia un bug del compilatore per questo caso d'angolo. Le regole di risoluzione del sovraccarico sono complesse ma questo caso sembra essere uno dei più comuni. –

+1

Ci si può aspettare un senso con i bug del compilatore? Costringere il compilatore a generare una rappresentazione simbolica leggermente diversa può facilmente nascondere l'errore. – Suma

0

Il compilatore C++ sintetizza il costruttore di copie predefinito nella seguente situazione. (Da Inside C++ Object Model)

  1. Quando la classe contiene un oggetto membro di una classe per la quale esiste un costruttore di copie.
  2. Quando la classe è derivata da una classe base per la quale esiste un costruttore di copie.
  3. Quando la classe dichiara una o più funzioni virtuali
  4. Quando la classe è derivata da una catena di ereditarietà in cui una o più classi di base sono virtuali.

Possiamo vedere che la classe A non è nelle 4 situazioni. Quindi cl non sintetizzare il costruttore di copie predefinito per questo. Forse è per questo che 2 oggetti temporanei vengono costruiti e distrutti.

Dalla finestra disassiale, possiamo vedere il seguente codice, non A :: A chiamato.:

B b; 
00B317F8 lea   ecx,[b] 
00B317FB call  B::B (0B31650h) 
00B31800 mov   dword ptr [ebp-4],0 
func(b); 
00B31807 mov   al,byte ptr [ebp-12h] 
00B3180A mov   byte ptr [ebp-13h],al 
00B3180D mov   byte ptr [ebp-4],1 
00B31811 movzx  ecx,byte ptr [ebp-13h] 
00B31815 push  ecx 
00B31816 call  func (0B31730h) 

Ma se rendiamo virtuale il distruttore. Otterremo il seguente codice di disassemblaggio, possiamo vedere che A :: A è chiamato. Quindi il risultato è come previsto, solo 1 oggetto creato.

B b; 
00331898 lea   ecx,[b] 
0033189B call  B::B (03316A0h) 
003318A0 mov   dword ptr [ebp-4],0 
func(b); 
003318A7 push  ecx 
003318A8 mov   ecx,esp 
003318AA mov   dword ptr [ebp-1Ch],esp 
003318AD lea   eax,[b] 
003318B0 push  eax 
003318B1 call  A::A (0331900h) 
003318B6 mov   dword ptr [ebp-20h],eax 
003318B9 call  func (03317D0h)