網站首頁 編程語言 正文
??每一個不曾起舞的日子都是對生命的辜負
??寫在前面
上一節解決了類與對象封裝的問題,這一節就是對象的初始化和清理的構造函數與析構函數的內容了;對象的初始化和清理也是兩個非常重要的安全問題:一個對象或者變量沒有初始狀態,對其使用后果是未知,同樣的使用完一個對象或變量,沒有及時清理,也會造成一定的安全問題;c++利用了構造函數和析構函數解決上述問題,這兩個函數將會被編譯器自動調用,完成對象初始化和清理工作。對象的初始化和清理工作是編譯器強制要我們做的事情,因此如果我們不提供構造和析構,編譯器提供編譯器提供的構造函數和析構函數是空實現。下面開始正文:
??目錄
構造函數和析構函數
語法
作用?
代碼實現
兩大分類方式
三種調用方式
括號法
顯示法
隱式轉換法
正確調用拷貝構造函數
正常調用
值傳遞的方式給函數參數傳值
值傳遞方式返回局部對象
構造函數的調用規則
??總結
構造函數和析構函數
語法
構造函數語法: 類名(){}
1、沒有返回值也不寫void
2、函數名稱與類名相同
3、構造函數可以有參數,因此可以發生重載
4、程序在調用對象時會自動調用,無需手動調用且只會調用一次
析造函數語法: ~類名(){}
1、沒有返回值也不寫void
2、函數名稱與類名相同,在名稱前加上符號~
3、構造函數不可以有參數,因此不可以發生重載
4、程序在對象銷毀前會自動調用析構,無需手動調用且只會調用一次
作用?
構造函數 ?主要作用于創建對象時為對象的成員屬性賦值,構造函數由編譯器自動調用,無須手動調用
析構函數 ?主要作用于對象銷毀前系統自動調用,執行一些清理工作
代碼實現
#include
using namespace std;
class Person
{
public:
Person()
{
cout << "Person構造函數的調用" << endl;
}
~Person()
{
cout << "~Person析構函數的調用" << endl;
}
};
void test01()
{
Person p;//棧上的對象運行完畢后,編譯器自動釋放
}
int main()
{
test01();
}
test01中創建了Person類p,主函數只是調用了一下創建的Person類p,編譯器就自動調用了類的構造函數和析構函數,析構函數是程序運行完畢后,編譯器自動清理內存空間的時候調用的。
兩大分類方式
按參數分為 有參構造 和 無參構造
按類型分為 普通構造 和 拷貝構造
無參和有參構造很好理解,就是有無參數的區別,這里講一下拷貝構造函數:
//拷貝構造函數
Person(const Person &p) //格式: const 類名 引用(&)變量名
{
//講傳入的人身上的所有屬性,拷貝到我身上
age = p.age;
cout << "Person的拷貝構造函數調用" << endl;
}
Person()的括號中是const Person &p,這是拷貝構造的函數格式,他需要傳入相同類的對象,會產生一個具有相同屬性的類,比如p1的年齡為20,經過拷貝構造p2的年齡也會是20,但是兩個類對象的地址并不相同,這個到后面會具體解釋
三種調用方式
class Person
{
public:
//構造函數
Person()
{
cout << "Person的無參構造函數調用" << endl;
}
Person(int a)
{
age = a;
cout << "Person的有參構造函數調用" << endl;
}
//拷貝構造函數
Person(const Person &p) //格式: const 類名 引用(&)變量名
{
//講傳入的人身上的所有屬性,拷貝到我身上
age = p.age;
cout << "Person的拷貝構造函數調用" << endl;
}
~Person()
{
cout << "~Person的析構函數調用" << endl;
}
int age;
};
括號法
Person p;//默認構造函數調用
Person p2(10);//有參構造函數
Person p3(p2);//拷貝構造函數
cout << "p2 age=" << p2.age << endl;
cout << "p3 age=" << p3.age << endl;
注意事項:調用默認構造函數的時候,不要加();Person p1() 編譯器會認為是函數的聲明,不認為在創建對象,等同于 void func()?
顯示法
Person p;
Person p2=Person(10);//有參構造函數
Person p3=Person(p2);//拷貝構造函數
Person(100);//匿名對象,特點:當前執行完畢后,系統會立即回收掉匿名對象
cout << "AAAAA" << endl;
注意事項2:拷貝構造初始化匿名對象等同于去掉括號,導致重定義,不要用拷貝構造初始化匿名對象,如果利用匿名對象的話,會和Peron p2=Person(10),重復,出現重定義錯誤;也不要用拷貝構造初始化匿名對象。
隱式轉換法
Person p2 = 10;// 有參構造函數
Person p3 = p2;// 拷貝構造函數
這個方法不推薦使用,調用的很不明顯,建議使用前面兩個方法調用構造函數。
正確調用拷貝構造函數
class Person
{
public:
Person()
{
cout << "Person的無參構造函數調用" << endl;
}
Person(int a)
{
m_age = a;
cout << "Person的有參構造函數調用" << endl;
}
Person(const Person& p)
{
m_age = p.m_age;
cout << "Person的拷貝構造函數調用" << endl;
}
~Person()
{
cout << "Person 的析構函數調用" << endl;
}
int m_age;
};
正常調用
void test01()
{
Person p1(20);
Person p2(p1);
cout << "p2的年齡為:" << p2.m_age << endl;
}
主函數中直接調用test01,這時候會顯示 p2的年齡為20,并且打印:拷貝構造函數的調用。所以說,使用一個已經創建完畢的對象來初始化一個新對象的時候會調用拷貝構造函數
值傳遞的方式給函數參數傳值
void doWork(Person p)
{ }
void test02()
{
Person p;
doWork(p);
}
大家可以猜一下,在主函數調用,會運行出什么結果,答案是:無參構造函數調用和拷貝構造函數調用,最后是兩個析構函數調用;淺析一下過程,調用test02時創建了對象P,所以自動調用無參構造函數,當運行到doWork(p)時,調用拷貝構造函數,隨后拷貝構造函數被清理,調用析構函數,程序結束前,p被清理,再次調用析構函數,程序結束。
值傳遞方式返回局部對象
Person doWork2()
{
Person p1;
cout << (int)&p1<<" 1 " << endl;
return p1;//返回就拷貝構造函數,隨后釋放掉,調用析構
}
void test03()
{
Person p = doWork2();//重新創建局部對象,并不是上面返回的對象p1
cout << (int)&p<<" 2 " << endl;
}
這里doWork2返回值時Person類型,也就是說return p1后會拷貝構造其屬性給test03調用的p,但是p1和p2并不是同一個對象,我們可以輸出他們的地址來驗證。
這里的調用順序是:Person P1 的無參構造,隨后輸出p1地址,然后返回值的時候先調用拷貝構造函數,把值賦給p,隨后清理p1調用析構;然后回到test03中,輸出p的地址,程序結束前調用析構,程序結束。
構造函數的調用規則
編譯器提供:
1、創建一個類,c++編譯器會給每個類都至少添加三個函數
默認構造(空實現)
析構函數(空實現)
值拷貝構造(值拷貝)2、如果我們寫了有參構造,編譯器不再提供默認構造,但是提供值拷貝構造
? ? ? 如果我們寫了拷貝構造函數,編譯器不再提供其他普通構造函數
void test01()
{
Person p1;
p1.m_age = 19;
Person p2(p1);//即使沒寫拷貝構造仍然能得到結果p2.m_age =19
cout << "p2的年齡為:" << p2.m_age << endl;
}
也就是說,就算我們不寫無參和拷貝構造,調用test03也會得到值拷貝后的p2年齡,這是編譯器默認提供的三個函數。但是如果寫了有參構造,Person p1這行代碼就會報錯,提示找不到默認構造函數;同樣的如果自己寫了拷貝構造,Person p1也會顯示同樣的錯誤。
??總結
這篇博文講了一部分對象的初始化和清理的內容,著重講了構造函數的調用方法、規則,以及拷貝構造函數的概念,調用方法和細節。下一篇直接準備深淺拷貝的內容和初始化列表,靜態成員等的問題,徹底結束對象的初始化和清理內容,期待下篇與你們見面!
原文鏈接:https://blog.csdn.net/m0_58618795/article/details/124903817
相關推薦
- 2022-02-25 C++構造函數的初始化列表詳解_C 語言
- 2022-11-12 C++?哈希表的基本用法及說明_C 語言
- 2022-10-04 SQL語句中的DDL類型的數據庫定義語言操作_MsSql
- 2023-01-07 C++中function的實現原理詳解_C 語言
- 2022-08-15 當添加一個鍵值對元素時,HashMap發生了什么?
- 2022-12-11 C語言如何計算兩個數的最小公倍數_C 語言
- 2022-05-21 如何使用rust實現簡單的單鏈表_相關技巧
- 2023-06-19 阿里低代碼框架lowcode-engine設置默認容器詳解_React
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支