C++類的構造與析構特點及作用詳解
一、類的構造函數
什么是構造函數
和類具有相同名稱,并且沒有返回值類型的函數,就是類的構造函數
概念模糊、直接舉例:
#include <stdio.h> #include <windows.h> struct Test { Test() // 和類具有相同的名、并且沒有返回值 { } }; int main() { return 0; }
構造函數的特點
直接先來說特點吧,然后論證:
1、構造函數在定義對象的時候被調用
2、構造函數可以進行函數重載,可以有很多個
3、創(chuàng)建對象時默認調用的是無參構造
證明1:
構造函數在定義對象的時候被調用;
論證如下:
#include <stdio.h> struct Test { Test() { printf("你調用了構造函數\n"); // 此處下斷點、如果該函數被調用肯定會停下來。 } }; int main() { Test te; printf("接力\n"); return 0; }
我們在Test()構造的輸出語句上加斷點、當程序調用Test的printf肯定會停下來,這個時候我們轉到反匯編,單步步過、直到函數返回之后,就能知到剛剛是在哪里調用的構造函數了
vs2010:F7編譯、F5調試、ALT+8反匯編:
F10一直運行到返回:
這里編譯器做了優(yōu)化,可以直接看出來是在Test定義對象的時候調用了構造。
證明2:
構造函數可以進行函數重載,可以有很多個;
論證如下:
#include <stdio.h> #include <Windows.h> struct Test { Test() { printf("你調用了構造函數\n"); // 此處下斷點、如果該函數被調用肯定會停下來。 } Test(int a) { printf("重載%d\n",a); } Test(int a, int b) { printf("重載%d\n",a+b); } }; int main() { Test te; Test te1(1); Test te2(1,1); // 注意、調用有參的構造函數時,需要傳遞參數 system("pause"); return 0; }
重載了兩個,注意:調用有參數的構造時,需要傳遞參數。
運行可以通過:
證明3:
創(chuàng)建對象時默認調用的是無參構造;
論證如下:
#include <stdio.h> #include <Windows.h> struct Test { Test(int a) { printf("重載%d\n",a); } Test(int a, int b) { printf("重載%d\n",a+b); } }; int main() { Test te; // 普通定義對象的方式、不帶參數 system("pause"); return 0; }
首先我們刪除無參構造,看看能否編譯通過:
不可以
然后刪除有參構造:
#include <stdio.h> #include <Windows.h> struct Test { Test() { printf("你調用了構造函數\n"); // 此處下斷點、如果該函數被調用肯定會停下來。 } }; int main() { Test te; system("pause"); return 0; }
可以
全部都加上:
#include <stdio.h> #include <Windows.h> struct Test { Test() { printf("你調用了構造函數\n"); // 此處下斷點、如果該函數被調用肯定會停下來。 } Test(int a) { printf("重載%d\n",a); } Test(int a, int b) { printf("重載%d\n",a+b); } }; int main() { Test te; system("pause"); return 0; }
運行結果:
這已經證明了,Test te;平常這樣定義對象的時候,調用的是無參構造。如果需要調用有參構造,必須傳入參數;
這個特點很簡單、有參函數肯定要傳參嘛,所以定義對象的時候肯定要傳入參數?。?/p>
但是這里建議無論什么時候寫類,最好還是寫上無參構造,哪怕什么都不做也盡量寫上,避免不必要的麻煩。
構造函數的作用
一般用于初始化類的成員
如下:
#include <stdio.h> #include <Windows.h> struct Test { int x; int y; int z; Test() { } Test(int x,int y,int z) // 構造函數初始化對象 { this->x = x; this->y = y; this->z = z; } }; int main() { Test te; Test te1(1,2,3); // 定義對象并調用有參構造進行初始化 printf("%d %d %d\n",te1.x,te1.y,te1.z); // 輸出看看是否初始化成功 system("pause"); return 0; }
運行如下:
初始化成功。
二、類的析構函數
什么是析構函數
類的構造函數名前加上'~'這個符號,就是類的析構函數
概念模糊、代碼如下:
#include <stdio.h> #include <Windows.h> struct Test { Test() { printf("你調用了一次類的構造函數\n"); } ~Test() { printf("你調用了一次類的析構函數\n"); } }; int main() { Test te; // system("pause"); // 這里就不要讓程序停下來了,不然析構不了 return 0; }
~Test(){}就這個樣子
析構函數的特點
依然直接先來說特點,然后論證:
1、析構函數不能重載、不能有參數
2、析構函數在變量聲明周期結束時被調用
3、析構函數被調用分兩種情況:堆棧中定義的對象、全局區(qū)中定義的對象
證明1:
析構函數不能重載、不能有參數;
編譯不通過。
既然不能有參數,那重載更不可能了
證明成功。
證明2:
析構函數在變量聲明周期結束時被調用;
局部變量的生命周期是在一個大括號內,即一個所處塊結束。
所以:
#include <stdio.h> #include <Windows.h> struct Test { Test() { printf("你調用了一次類的構造函數\n"); } ~Test() { printf("你調用了一次類的析構函數\n"); } }; int main() { { Test te; printf("te生命周期即將結束。\n"); } // 析構應該在這里被調用 printf("te生命周期結束。\n"); system("pause"); return 0; }
運行結果如下:
斷點
結果
證明成功。
證明3:
析構函數被調用分兩種情況:堆棧中定義的對象、全局區(qū)中定義的對象;
已知堆棧中定義的對象(局部變量)在塊語句結束之后就會被調用,那么帶有return的main函數是在返回前調用析構,還是返回后呢?
代碼如下:
#include <stdio.h> #include <Windows.h> struct Test { Test() { printf("你調用了一次類的構造函數\n"); } ~Test() { printf("你調用了一次類的析構函數\n"); // 斷點--匯編 } }; int main() { Test te; // system("pause"); // 不要使用pause,不然無法返回 return 0; }
斷點-調試-匯編:
可以看到是在函數返回前被調用的。
如果在全局區(qū)定義的對象呢?
這個問題很難說,像我一樣定義兩個斷點就行了,如下:
#include <stdio.h> #include <Windows.h> struct Test { Test() { printf("你調用了一次類的構造函數\n"); } ~Test() // 斷點 { printf("你調用了一次類的析構函數\n"); } }; Test te; int main() { // system("pause"); // 不要使用pause,不然無法返回 return 0; // 斷點 }
運行:
發(fā)現(xiàn)一運行就斷在了return,當我們發(fā)F10繼續(xù)運行的時候,并沒有直接調用析構,而是到了括號那里:
繼續(xù)F10:
通過翻譯,可以得知這就是進程結束時的一些收尾工作。
繼續(xù)F10:
現(xiàn)在大概可以總結了,類的對象定義為全局變量時,是在main函數結束之后進程退出之前,調用的析構函數。
小結
當類的對象定義為局部變量時(堆棧),定義這個對象的塊作用域結束時就會調用該對象的析構函數,如果在main函數這個塊作用域中定義的對象,那么就是在return之前調用析構。
當類的對象定義為全局變量時(全局區(qū)),會在main函數return函數返回之后和進程結束之前,調用該對象的析構函數。
析構函數的作用
我們知道了析構函數都是在類的對象生命周期結束時被調用,那么就代表下面不會再使用到這個對象;所以析構函數一般用于一些收尾的工作,以防忘記。
比如當你使用了該對象的成員申請了內存(malloc、new等)、或者open了一些文件,那么可以在析構函數中free delete 或者close。
例如:
#include <stdio.h> #include <Windows.h> struct Test { int x; char* name; Test() { name = (char*)malloc(sizeof(char)*20); // 構造時動態(tài)申請 } ~Test() { if(this->name!=0) // 析構時判斷是個否為空,不為空釋放 { free(name); name = 0; } } }; int main() { Test te; return 0; }
這里我就不運行了,大家可以自己測試下。
總結
構造函數
1、和類具有相同名稱,并且沒有返回值類型的函數,就是類的構造函數
2、構造函數在定義對象的時候被調用
3、構造函數可以進行函數重載,可以有很多個
4、創(chuàng)建對象時默認調用的是無參構造
析構函數
1、類的構造函數名前加上'~'這個符號,就是類的析構函數
2、析構函數不能重載、不能有參數
3、析構函數在變量聲明周期結束時被調用
4、析構函數被調用分兩種情況:堆棧中定義的對象、全局區(qū)中定義的對象
到此這篇關于C++類的構造與析構特點及作用詳解的文章就介紹到這了,更多相關C++類的構造與析構內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解c++ atomic原子編程中的Memory Order
在多核編程中,我們使用內核對象【如:事件對象(Event)、互斥量對象(Mutex,或互斥體對象)、信號量對象(Semaphore)等】來避免多個線程修改同一個數據時產生的競爭條件。本文將詳細介紹c++ atomic原子編程中的Memory Order。2021-06-06