【 聲明:著作權,歡迎轉載,請勿用於商業用途。 聯絡信箱:feixiaoxing @163.com】
拷貝建構函式和複製函數是類裡面比較重要的兩個函數。兩者有什麼區別呢?其實也很簡單,我們可以舉個例子,加入有這樣一個類的定義:
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;}};
那麼我們在如下的函數裡面進行調用的時候,調用的函數分別是哪些呢?
void process(){apple a, c;apple b =a;c = b;}
其實彙編的結果是這樣的,大家可以一起看一下,自己嘗試讀一下。如果一次不是很明白,可以多讀幾次。
70: apple a, c;0040127D lea ecx,[ebp-10h]00401280 call @ILT+70(apple::apple) (0040104b)00401285 mov dword ptr [ebp-4],00040128C lea ecx,[ebp-14h]0040128F call @ILT+70(apple::apple) (0040104b)00401294 mov byte ptr [ebp-4],171: apple b =a;00401298 lea eax,[ebp-10h]0040129B push eax0040129C lea ecx,[ebp-18h]0040129F call @ILT+50(apple::apple) (00401037)004012A4 mov byte ptr [ebp-4],272: c = b;004012A8 lea ecx,[ebp-18h]004012AB push ecx004012AC lea ecx,[ebp-14h]004012AF call @ILT+75(apple::operator=) (00401050)73: }004012B4 mov byte ptr [ebp-4],1004012B8 lea ecx,[ebp-18h]004012BB call @ILT+0(apple::~apple) (00401005)004012C0 mov byte ptr [ebp-4],0004012C4 lea ecx,[ebp-14h]004012C7 call @ILT+0(apple::~apple) (00401005)004012CC mov dword ptr [ebp-4],0FFFFFFFFh004012D3 lea ecx,[ebp-10h]004012D6 call @ILT+0(apple::~apple) (00401005)004012DB mov ecx,dword ptr [ebp-0Ch]004012DE mov dword ptr fs:[0],ecx004012E5 pop edi004012E6 pop esi004012E7 pop ebx004012E8 add esp,58h004012EB cmp ebp,esp004012ED call __chkesp (004087c0)004012F2 mov esp,ebp004012F4 pop ebp004012F5 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】,這正好和變數出現的順序相反。所以我們看到,解構函式和建構函式是嚴格一一對應的,誰先出現,誰後析構。
【預告: 下面的部落格我們會對構造、析構中出現的一些現象進行總結】