2012-11-14 58 views
23

這裏是在g ++ 4.7和vs2012(cl17)中產生不同輸出的代碼。C++:按值複製函數params在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; 
} 

的GCC輸出是13242,而CL輸出132242

爲什麼cl編譯器產生第二個A對象,同時它在堆棧上創建一個副本,以及爲了什麼目的?

+2

測試它在VS2010,結果是「132242」 – Apokal

+1

鐺4.1生產「13242」 –

+4

VS發行版只生產13242,但沒有調試版本 – billz

回答

-3

您遇到了編譯器的錯誤。


func需要創建對象的副本(但要注意切片)功能:

適當的功能來進行說明。

那麼,什麼情況是這樣的:

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

當拷貝構造對象的獲取在func通話結束時銷燬多餘〜A()調用。

+5

在「cl輸出」中,在'func()'末尾有兩個對'〜A'的調用。這似乎是一個問題。 –

+0

@KevinBallard我解釋了會發生什麼。我會將行爲稱爲編譯器的錯誤。有些人會稱之爲擴展;) –

+2

@BЈовић應該發生什麼事情已經在問題中了;這只是一個重申。 – Gorpik

5

它似乎是一個編譯器錯誤。
C++標準不使用術語對象切片,您正在將類型B的對象傳遞給接收類型爲A的參數的函數。編譯器將應用通常的重載解析來找到適當的匹配。在這種情況下:
基類A提供了編譯器提供的拷貝構造函數,它將引用A,並且在沒有其他轉換函數的情況下,這是最佳匹配,應該由編譯器使用。

請注意,如果有更好的轉換可用,它將被使用。例如:如果A有一個構造函數A::A(B const&),除了複製構造函數之外,將使用此構造函數,而不是複製構造函數。

+4

「A」的顯式微小副本構造函數解決了VS2010中的問題,這對我來說沒有多大意義。 – Gorpik

+1

@Gorpik:更多的理由相信這是一個編譯器錯誤的情況下。重載解析規則是複雜的,但這種情況似乎是一個更常見的。 –

+1

可以期待編譯器錯誤的感覺嗎?強制編譯器生成一個不同的符號表示可以很容易地隱藏錯誤。 – Suma

0

C++編譯器將在下列情況下合成默認的拷貝構造函數。 (來自C++對象模型內部)

  1. 當這個類包含一個複製構造函數存在的類的成員對象時。
  2. 當這個類是從一個拷貝構造函數存在的基類派生的。
  3. 當該類聲明一個或多個虛擬函數時
  4. 當該類是從繼承鏈派生的,其中一個或多個基類是虛擬的。

我們可以看到A類不在4種情況下。所以cl不合成它的默認拷貝構造函數。也許這就是爲什麼2個臨時A對象被構建和銷燬的原因

從disassemly窗口,我們可以看到下面的代碼,沒有A :: A調用。:

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) 

但是,如果我們使析構函數爲虛擬。我們將得到以下反彙編代碼,我們可以看到A :: A被調用。然後結果如預期的那樣,只創建1個對象。

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)