C++類中三大函數詳解(構造、析構和拷貝)
前言
今天和大傢分享一下C++中在創建類的時候編譯器會提供給一個類至少三個函數,分別是默認構造函數,析構函數,拷貝構造函數。
一.構造函數
1.構造函數的作用
我們在創建好類的對象之後,首先對它的每一個成員屬性賦值之後再對它們進行輸出操作,如果不賦值就輸出,這些值就會是垃圾值。而為瞭代碼的簡介,一次性為所有成員屬性初始化,C++的類提供瞭這樣的一個函數—構造函數。
2.構造函數的語法 類名(){}
1)構造函數,沒有返回值也不寫void
2) 函數名稱與類名相同
3)構造函數可以有參數,因此可以發生重載
4)程序在調用對象時候會自動調用構造,無須手動調用,而且隻會調用一次
二.析構函數
1.析構函數的作用
析構函數的作用與構造函數相反,一般是執行對象的清理工作,當對象的生命周期結束的時候,會自動的調用。析構函數的作用並不是刪除對象,在對象撤銷它所占用的內存之前,做一些清理的工作。清理之後,這部分內存就可以被系統回收再利用瞭。在設計這個類的時候,系統也會默認的提供一個析構函數。在對象的生命周期結束的時候,程序就會自動執行析構函數來完成這些工作。同構造函數,用戶自己定義,系統自動調用。
2.析構函數的語法~類名(){}
1)析構函數,沒有返回值也不寫void
2) 函數名稱與類名相同,在名稱前加上符號 ~
3)析構函數不可以有參數,因此不可以發生重載
4) 程序在對象銷毀前會自動調用析構,無須手動調用,而且隻會調用一次
代碼演示:
class Person { public: //構造函數 Person() { cout << "Person的構造函數調用" << endl; } //析構函數 ~Person() { cout << "Person的析構函數調用" << endl; } }; void test01() { Person p; } int main() { test01(); system("pause"); return 0; }
三.構造函數的分類及調用
1.兩種分類方式:
按參數分為: 有參構造和無參構造
按類型分為: 普通構造和拷貝構造
2.三種調用方式:
括號法
顯示法
隱式轉換法
//1、構造函數分類 // 按照參數分類分為 有參和無參構造 無參又稱為默認構造函數 // 按照類型分類分為 普通構造和拷貝構造 class Person { public: //無參(默認)構造函數 Person() { cout << "無參構造函數!" << endl; } //有參構造函數 Person(int a) { age = a; cout << "有參構造函數!" << endl; } //拷貝構造函數 Person(const Person& p) { age = p.age; cout << "拷貝構造函數!" << endl; } //析構函數 ~Person() { cout << "析構函數!" << endl; } public: int age; }; //2、構造函數的調用 //調用無參構造函數 void test01() { Person p; //調用無參構造函數 } //調用有參的構造函數 void test02() { //2.1 括號法,常用 Person p1(10); //註意1:調用無參構造函數不能加括號,如果加瞭編譯器認為這是一個函數聲明 //Person p2(); //2.2 顯式法 Person p2 = Person(10); Person p3 = Person(p2); //Person(10)單獨寫就是匿名對象 當前行結束之後,馬上析構 //2.3 隱式轉換法 Person p4 = 10; // Person p4 = Person(10); Person p5 = p4; // Person p5 = Person(p4); //註意2:不能利用 拷貝構造函數 初始化匿名對象 編譯器認為是對象聲明 //Person p5(p4); } int main() { test01(); //test02(); system("pause"); return 0; }
四.拷貝構造函數調用時機
C++中拷貝構造函數調用時機通常有三種情況
1.使用一個已經創建完畢的對象來初始化一個新對象
2.值傳遞的方式給函數參數傳值
3.以值方式返回局部對象
class Person { public: Person() { cout << "無參構造函數!" << endl; mAge = 0; } Person(int age) { cout << "有參構造函數!" << endl; mAge = age; } Person(const Person& p) { cout << "拷貝構造函數!" << endl; mAge = p.mAge; } //析構函數在釋放內存之前調用 ~Person() { cout << "析構函數!" << endl; } public: int mAge; }; //1. 使用一個已經創建完畢的對象來初始化一個新對象 void test01() { Person man(100); //p對象已經創建完畢 Person newman(man); //調用拷貝構造函數 Person newman2 = man; //拷貝構造 //Person newman3; //newman3 = man; //不是調用拷貝構造函數,賦值操作 } //2. 值傳遞的方式給函數參數傳值 //相當於Person p1 = p; void doWork(Person p1) {} void test02() { Person p; //無參構造函數 doWork(p); } //3. 以值方式返回局部對象 Person doWork2() { Person p1; cout << (int *)&p1 << endl; return p1; } void test03() { Person p = doWork2(); cout << (int *)&p << endl; } int main() { //test01(); //test02(); test03(); system("pause"); return 0; }
五.構造函數調用規則
默認情況下,c++編譯器至少給一個類添加3個函數
1.默認構造函數(無參,函數體為空)
2.默認析構函數(無參,函數體為空)
3.默認拷貝構造函數,對屬性進行值拷貝
構造函數調用規則如下:
如果用戶定義有參構造函數,c++不在提供默認無參構造,但是會提供默認拷貝構造
如果用戶定義拷貝構造函數,c++不會再提供其他構造函數
class Person { public: //無參(默認)構造函數 Person() { cout << "無參構造函數!" << endl; } //有參構造函數 Person(int a) { age = a; cout << "有參構造函數!" << endl; } //拷貝構造函數 Person(const Person& p) { age = p.age; cout << "拷貝構造函數!" << endl; } //析構函數 ~Person() { cout << "析構函數!" << endl; } public: int age; }; void test01() { Person p1(18); //如果不寫拷貝構造,編譯器會自動添加拷貝構造,並且做淺拷貝操作 Person p2(p1); cout << "p2的年齡為: " << p2.age << endl; } void test02() { //如果用戶提供有參構造,編譯器不會提供默認構造,會提供拷貝構造 Person p1; //此時如果用戶自己沒有提供默認構造,會出錯 Person p2(10); //用戶提供的有參 Person p3(p2); //此時如果用戶沒有提供拷貝構造,編譯器會提供 //如果用戶提供拷貝構造,編譯器不會提供其他構造函數 Person p4; //此時如果用戶自己沒有提供默認構造,會出錯 Person p5(10); //此時如果用戶自己沒有提供有參,會出錯 Person p6(p5); //用戶自己提供拷貝構造 } int main() { test01(); system("pause"); return 0; }
六.深拷貝與淺拷貝
淺拷貝:簡單的賦值拷貝操作
深拷貝:在堆區重新申請空間,進行拷貝操作
示例:
class Person { public: //無參(默認)構造函數 Person() { cout << "無參構造函數!" << endl; } //有參構造函數 Person(int age ,int height) { cout << "有參構造函數!" << endl; m_age = age; m_height = new int(height); cout << "拷貝構造函數!" << endl; //如果不利用深拷貝在堆區創建新內存,會導致淺拷貝帶來的重復釋放堆區問題 m_age = p.m_age; m_height = new int(*p.m_height) } //拷貝構造函數 Person(const Person& p) { ; } //析構函數 ~Person() { cout << "析構函數!" << endl; if (m_height != NULL) { delete m_height; } } public: int m_age; int* m_height; }; void test01() { Person p1(18, 180); Person p2(p1); cout << "p1的年齡: " << p1.m_age << " 身高: " << *p1.m_height << endl; cout << "p2的年齡: " << p2.m_age << " 身高: " << *p2.m_height << endl; } int main() { test01(); system("pause"); return 0; }
總結:
如果屬性有在堆區開辟的,一定要自己提供拷貝構造函數,防止淺拷貝帶來的問題
到此這篇關於C++類中三大函數(構造、析構和拷貝)的文章就介紹到這瞭,更多相關C++類的三大函數內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!