網(wǎng)站首頁 編程語言 正文
了解C++默默編寫哪些函數(shù)
當(dāng)實(shí)現(xiàn)一個(gè)空類,c++會(huì)為你補(bǔ)上構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),拷貝賦值運(yùn)算符,析構(gòu)函數(shù)
class Empty{};
//等于你寫了
class{
public:
Empty(){...};
Empty(const Empty& rhs){...};
~Empty(){...};
Empty& operator=(const Empty& rhs){...}
};
當(dāng)這些函數(shù)被調(diào)用是,才會(huì)被編譯器創(chuàng)建出來。如果你自己聲明了一個(gè)構(gòu)造函數(shù),編譯器將不會(huì)創(chuàng)建默認(rèn)構(gòu)造函數(shù)。
當(dāng)然,編譯器有時(shí)會(huì)拒絕生成operator=
class A{
public:
NameObject{string& name};
private:
string& nameValue;//為一個(gè)字符串引用
}
string newDog("peter");
string oldDog("fx")
A a(newDog);
A b(olddog);
a=b;//此處賦值就會(huì)導(dǎo)致a的nameValue綁定到不同對(duì)對(duì)象上面 而引用一旦綁定無法更改 是錯(cuò)誤的
所以如果打算在一個(gè)含有引用成員的class中支持賦值操作,必須要自己定義operator=()
不想使用編譯器函數(shù)
若不想使用編譯器自動(dòng)生成的函數(shù),就該明確拒絕
如果一個(gè)類你想讓它是獨(dú)一無二,無法被拷貝的,則需要拒絕編譯器生成的copy構(gòu)造函數(shù)和copy賦值運(yùn)算符。
方法一:將copy構(gòu)造函數(shù)和copy賦值運(yùn)算符函數(shù)聲明為private并且故意不實(shí)現(xiàn)
class A{
public:
...
private:
A(const A&);
A& operator=(const A&);//只聲明不實(shí)現(xiàn)
}
因?yàn)樗芯幾g器生成的版本都是public。聲明一個(gè)成員函數(shù),阻止了編譯器自己創(chuàng)建它,而聲明為private,又可以阻止別人調(diào)用它。
客戶試圖拷貝A對(duì)象時(shí),編譯器會(huì)阻止,而當(dāng)在member或friend函數(shù)之內(nèi)調(diào)用時(shí),連接器阻止.
方法二:寫一個(gè)父類,將copy構(gòu)造函數(shù)和copy賦值運(yùn)算符函數(shù)聲明為private并且故意不實(shí)現(xiàn),再繼承這個(gè)父類。
class A{
protected:
A(){}
~A(){}
private:
A(const A&);
A& operator=(const A&)
}
為多態(tài)基類聲明virtual析構(gòu)函數(shù)
我的理解是:如果不這樣做,在delete父類的指針的時(shí)候無法使用多態(tài)性質(zhì),只會(huì)刪除掉父類的部分,而子類的部分會(huì)無法刪除,造成局部銷毀。
class A{
public:
A();
~A();
};
class B:public A{};
//如果設(shè)計(jì)一個(gè)函數(shù) 返回一個(gè)基類的指針 可以使用多態(tài) 來自動(dòng)判斷是調(diào)用A還是B的析構(gòu)來刪除對(duì)象
A* base_pointer=getPointer();
delete base_pointer;
//當(dāng)子類用一個(gè)指向父類的指針來執(zhí)行刪除,若父類的析構(gòu)函數(shù)不是virtual,則無法調(diào)用子類的析構(gòu)函數(shù)
//導(dǎo)致只刪除 子類中父類的部分 剩下子類獨(dú)有的部分
所以:無虛不基
base class的析構(gòu)函數(shù)一定得是vritual,且可以推廣到其他函數(shù),若一個(gè)class里面沒有一個(gè)virtual函數(shù),那它不適合當(dāng)一個(gè)base class。
class A{
public:
A();
virtual ~A();//應(yīng)該這么搞
};
class B:public A{};
//如果設(shè)計(jì)一個(gè)函數(shù) 返回一個(gè)基類的指針 可以使用多態(tài) 來自動(dòng)判斷是調(diào)用A還是B的析構(gòu)來刪除對(duì)象
A* base_pointer=getPointer();
delete base_pointer;
但是,析構(gòu)函數(shù)不能無端聲明為virtual,因?yàn)槁暶鳛関irtual需要虛表指針(vptr),vptr也是要占內(nèi)存的,會(huì)增加對(duì)象的體積,減緩運(yùn)行速度。
所以:當(dāng)class至少含有一個(gè)virtual函數(shù),才為它聲明vritual虛構(gòu)函數(shù)
當(dāng)然,也可以將虛構(gòu)函數(shù)聲明為純虛函數(shù),使該class成為一個(gè)抽象基類,注意:必須為這個(gè)純虛函數(shù)提供一份定義。因?yàn)槲鰳?gòu)函數(shù)是從派生類開始往基類調(diào)用,所以編譯器會(huì)在A的派生類的析構(gòu)函數(shù)中調(diào)用~A()。
class A{
public:
virtual~ A()=0;
}
A::~A(){}//必須為這個(gè)純虛函數(shù)提供一份定義
別讓異常逃離析構(gòu)函數(shù)
C++不喜歡析構(gòu)函數(shù)出現(xiàn)異常。
可以在發(fā)生異常時(shí)終止程序,也可以吞下發(fā)生的異常
A::~A()
{
try{ a.close();}
catch(...){
...
//制作運(yùn)轉(zhuǎn)記錄,記錄下close的失敗
std::abort();//終止程序
}
}
A::~A()
{
try{ a.close();}
catch(...){
...
//記錄下close的調(diào)用失敗
}
}
如果某個(gè)操作可能在失敗時(shí)拋出異常,而又必須要處理這個(gè)異常,這個(gè)異常必須來自析構(gòu)以外的函數(shù)。(這里其實(shí)不太理解,文中給的例子是用一個(gè)新的成員函數(shù)來直行關(guān)閉)
絕不在構(gòu)造和析構(gòu)過程中調(diào)用virtual函數(shù)
我的理解是:在構(gòu)造和析構(gòu)的過程中調(diào)用的virtual成員函數(shù)并沒有多態(tài)性質(zhì)(注意該虛函數(shù)不是指析構(gòu)函數(shù)和構(gòu)造函數(shù)是虛函數(shù),而是除此之外的一個(gè)成員函數(shù))
class A{
public:
A();
vritual void xxx() const =0;
};
A::A(){
...
xxx();
}
class B:public A{
public:
virtual xxx() const;
};
//當(dāng)執(zhí)行
B b;
派生類的base class成分會(huì)在派生類自身成分構(gòu)造之前先構(gòu)造,而A的構(gòu)造函數(shù)調(diào)用了虛函數(shù)xxx,這時(shí)xxx是A的xxx,而不會(huì)多態(tài)調(diào)用B的xxx,即使目前是在創(chuàng)建B對(duì)象。
根本原因是:在派生類的基類構(gòu)造期間,對(duì)象的類型是基類而不是派生類,只有當(dāng)派生類自己的部分開始執(zhí)行時(shí),該對(duì)象才變成一個(gè)派生類。
該道理同樣用于析構(gòu)函數(shù)
改法為:將A類的xxx改為non-vritual,在派生類的構(gòu)造函數(shù)傳遞必要信息給基類的構(gòu)造函數(shù)
class A{
public:
A();
void xxx() const =0;
};
A::A(){
...
xxx();
};
class B:public A{
public:
B(parameters):A(createXXX(parameters))
{...}
private:
static string createXXX(parameters);
};
在構(gòu)造期間,令派生類將必要的構(gòu)造信息向上傳遞給基類的構(gòu)造函數(shù)。
令operator=返回一個(gè)reference to *this
為了實(shí)現(xiàn)連鎖賦值,賦值操作符必須返回一個(gè)reference指向操作符的左側(cè),這是為class實(shí)現(xiàn)賦值操作符時(shí)必須遵守的協(xié)議。
//連鎖賦值
int x,y,z;
x=y=z=1;
class A{
public:
A& operator=(const A& rhs)
{
return *this;
}
}
在operator=中處理自我賦值
自我賦值發(fā)生在對(duì)象賦值給自己本身,例如*px=*py;
px和py都指向一個(gè)對(duì)象,則是一個(gè)自我賦值。
常發(fā)生在用引用賦值,指針賦值,多態(tài)等
可能會(huì)引發(fā)delete時(shí)將賦值和被賦值對(duì)象都刪除了
避免方法:
1、證同測試
A &A operator=(const A& rhs){
if(this==&rhs) return *this;//一樣則什么也不做 直接返回
...
}
2、調(diào)整語序
class B{...};
class A{
private:
B* pb;
}
A& A::operator=(const A& rhs){
B* p=pb;//先記住原先的pb
pb=new B(*rhs.pb);//令pb指向rhs.pb指向的一個(gè)副本,完成賦值
delete p;//刪除原來的pb
return *this;
}
3、使用copy and swap,不大推薦
class A{
...
void swap(A &rhs);
...
}
A& A::operator=(const A& rhs){
A temp(rhs);//拷貝rhs的副本
swap(temp);//交換
return *this;
}
復(fù)制對(duì)象時(shí)別忘了每個(gè)成分
如果自己定義了拷貝構(gòu)造函數(shù),編譯器將不會(huì)提醒你是否拷貝完所有成分,如果為class添加一個(gè)成員變量,則必須修改所有的copy函數(shù)和非標(biāo)準(zhǔn)形勢的operator=
給派生類寫copy函數(shù)的時(shí)候,也要復(fù)制它的基類的成分,那些成分往往是private,所以要讓派生類調(diào)用相應(yīng)的base class函數(shù)
B::B(const B &rhs):A(rhs),//調(diào)用基類的copy構(gòu)造
...//對(duì)派生類部分初始化
{}
B::operator=(const B& rhs){
A::operator=(rhs);//對(duì)基類部分賦值
...//對(duì)派生類部分賦值
return *this;
}
編寫copy函數(shù)時(shí):
1、復(fù)制所有l(wèi)ocal成員變量
2、調(diào)用所有base classes內(nèi)的適當(dāng)copying函數(shù)
且不要嘗試以某個(gè)copy函數(shù)實(shí)現(xiàn)另一個(gè)copy函數(shù)
原文鏈接:https://blog.csdn.net/RolleX/article/details/126869128
相關(guān)推薦
- 2022-11-06 解析PyCharm集成GitLab代碼倉的問題_python
- 2023-11-17 Linux CentOS如何修改root用戶密碼
- 2022-10-04 Python基礎(chǔ)之dict和set的使用詳解_python
- 2022-12-01 Apache?Doris?Colocate?Join?原理實(shí)踐教程_Linux
- 2023-03-19 C語言利用goto語句設(shè)計(jì)實(shí)現(xiàn)一個(gè)關(guān)機(jī)程序_C 語言
- 2022-07-03 C++詳細(xì)講解函數(shù)調(diào)用與Struct和CLass的區(qū)別_C 語言
- 2022-04-19 前端開發(fā)中幾種存儲(chǔ)方式詳解
- 2022-05-29 ASP.NET?Core使用HttpClient調(diào)用WebService_實(shí)用技巧
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支