【 聲明:版權所有,歡迎轉載,請勿用於商業用途。 聯系信箱:feixiaoxing @163.com】
拷貝構造函數和復制函數是類裡面比較重要的兩個函數。兩者有什麼區別呢?其實也很簡單,我們可以舉個例子,加入有這樣一個類的定義:
copy to clipboardprint?class apple
{
public:
apple() { printf("apple()!\n");}
apple(apple& a) { printf("copy apple()!\n");}
apple& operator=(apple& a) { printf("= apple()\n"); return *this;}
~apple() { printf("~apple()!\n");}
void print() const { return;}
};
class apple
{
public:
apple() { printf("apple()!\n");}
apple(apple& a) { printf("copy apple()!\n");}
apple& operator=(apple& a) { printf("= apple()\n"); return *this;}
~apple() { printf("~apple()!\n");}
void print() const { return;}
};
那麼我們在如下的函數裡面進行調用的時候,調用的函數分別是哪些呢?
copy to clipboardprint?void process()
{
apple a, c;
apple b =a;
c = b;
}
void process()
{
apple a, c;
apple b =a;
c = b;
} 其實匯編的結果是這樣的,大家可以一起看一下,自己嘗試讀一下。如果一次不是很明白,可以多讀幾次。
copy to clipboardprint?70: apple a, c;
0040127D lea ecx,[ebp-10h]
00401280 call @ILT+70(apple::apple) (0040104b)
00401285 mov dword ptr [ebp-4],0
0040128C lea ecx,[ebp-14h]
0040128F call @ILT+70(apple::apple) (0040104b)
00401294 mov byte ptr [ebp-4],1
71: apple b =a;
00401298 lea eax,[ebp-10h]
0040129B push eax
0040129C lea ecx,[ebp-18h]
0040129F call @ILT+50(apple::apple) (00401037)
004012A4 mov byte ptr [ebp-4],2
72: c = b;
004012A8 lea ecx,[ebp-18h]
004012AB push ecx
004012AC lea ecx,[ebp-14h]
004012AF call @ILT+75(apple::operator=) (00401050)
73: }
004012B4 mov byte ptr [ebp-4],1
004012B8 lea ecx,[ebp-18h]
004012BB call @ILT+0(apple::~apple) (00401005)
004012C0 mov byte ptr [ebp-4],0
004012C4 lea ecx,[ebp-14h]
004012C7 call @ILT+0(apple::~apple) (00401005)
004012CC mov dword ptr [ebp-4],0FFFFFFFFh
004012D3 lea ecx,[ebp-10h]
004012D6 call @ILT+0(apple::~apple) (00401005)
004012DB mov ecx,dword ptr [ebp-0Ch]
004012DE mov dword ptr fs:[0],ecx
004012E5 pop edi
004012E6 pop esi
004012E7 pop ebx
004012E8 add esp,58h
004012EB cmp ebp,esp
004012ED call __chkesp (004087c0)
004012F2 mov esp,ebp
004012F4 pop ebp
004012F5 ret
70: apple a, c;
0040127D lea ecx,[ebp-10h]
00401280 call @ILT+70(apple::apple) (0040104b)
00401285 mov dword ptr [ebp-4],0
0040128C lea ecx,[ebp-14h]
0040128F call @ILT+70(apple::apple) (0040104b)
00401294 mov byte ptr [ebp-4],1
71: apple b =a;
00401298 lea eax,[ebp-10h]
0040129B push eax
0040129C lea ecx,[ebp-18h]
0040129F call @ILT+50(apple::apple) (00401037)
004012A4 mov byte ptr [ebp-4],2
72: c = b;
004012A8 lea ecx,[ebp-18h]
004012AB push ecx
004012AC lea ecx,[ebp-14h]
004012AF call @ILT+75(apple::operator=) (00401050)
73: }
004012B4 mov byte ptr [ebp-4],1
004012B8 lea ecx,[ebp-18h]
004012BB call @ILT+0(apple::~apple) (00401005)
004012C0 mov byte ptr [ebp-4],0
004012C4 lea ecx,[ebp-14h]
004012C7 call @ILT+0(apple::~apple) (00401005)
004012CC mov dword ptr [ebp-4],0FFFFFFFFh
004012D3 lea ecx,[ebp-10h]
004012D6 call @ILT+0(apple::~apple) (00401005)
004012DB mov ecx,dword ptr [ebp-0Ch]
004012DE mov dword ptr fs:[0],ecx
004012E5 pop edi
004012E6 pop esi
004012E7 pop ebx
004012E8 add esp,58h
004012EB cmp ebp,esp
004012ED call __chkesp (004087c0)
004012F2 mov esp,ebp
004012F4 pop ebp
004012F5 ret
代碼有點長,大家可以一句一句來看,比如說就按照70、71、72、73分別查看對應的匯編代碼:
(1)70句: 我們看到函數做了兩次函數調用,恰好就是apple的構造函數調用。這也正好對應著兩個臨時變量a和c,兩個變量的地址分別是【ebp-10】和【ebp-14】,這裡也可以看出整個類的大小就是4個字節,就是一塊存放數據的普通內存。而構造函數之所以能和對應的內存綁定在一起,主要是因為ecx記錄了內存的起始地址,這在C++編譯中是十分關鍵的。我們看到的C++構造函數好像是沒有綁定內存,實際上在VC裡面已經做好了約定,ecx就是this指針,就是類的內存起始地址。有興趣的同學看看G++編譯的時候,采用的this指針是哪個寄存器保存的?(其實是eax)
(2)71句:通過對應看到了eax記錄了引用變量的地址,而ecx是ebp下面緊挨著四個字節。但是函數調用的地址和前面的缺省構造函數不太一樣,所以我們大膽猜測,這裡的構造函數這是拷貝構造函數,我們可以在調試的時候查看一下打印消息。
(3)72句:0x4012AF語句已經清楚地告訴了我們,這裡調用的函數就是operator=函數,這一部分是算術符重載的內容,我們在後面的博客會重點介紹。
(4)73句: 前面我們講過,析構函數在函數調用結束的時候被被自動調用,那麼這裡我們看到卻是出現了三個調用?這三個變量正好是我們之前說的a、b、c三個變量。那麼這三個變量調用的次序是怎樣的呢?我們可以查看一下變量的地址,分別是【ebp-18h】、【ebp-14h】、【ebp-10h】,這正好和變量出現的順序相反。所以我們看到,析構函數和構造函數是嚴格一一對應的,誰先出現,誰後析構。