網站首頁 編程語言 正文
了解C++默默編寫哪些函數
當實現一個空類,c++會為你補上構造函數,拷貝構造函數,拷貝賦值運算符,析構函數
class Empty{};
//等于你寫了
class{
public:
Empty(){...};
Empty(const Empty& rhs){...};
~Empty(){...};
Empty& operator=(const Empty& rhs){...}
};
當這些函數被調用是,才會被編譯器創建出來。如果你自己聲明了一個構造函數,編譯器將不會創建默認構造函數。
當然,編譯器有時會拒絕生成operator=
class A{
public:
NameObject{string& name};
private:
string& nameValue;//為一個字符串引用
}
string newDog("peter");
string oldDog("fx")
A a(newDog);
A b(olddog);
a=b;//此處賦值就會導致a的nameValue綁定到不同對對象上面 而引用一旦綁定無法更改 是錯誤的
所以如果打算在一個含有引用成員的class中支持賦值操作,必須要自己定義operator=()
不想使用編譯器函數
若不想使用編譯器自動生成的函數,就該明確拒絕
如果一個類你想讓它是獨一無二,無法被拷貝的,則需要拒絕編譯器生成的copy構造函數和copy賦值運算符。
方法一:將copy構造函數和copy賦值運算符函數聲明為private并且故意不實現
class A{
public:
...
private:
A(const A&);
A& operator=(const A&);//只聲明不實現
}
因為所有編譯器生成的版本都是public。聲明一個成員函數,阻止了編譯器自己創建它,而聲明為private,又可以阻止別人調用它。
客戶試圖拷貝A對象時,編譯器會阻止,而當在member或friend函數之內調用時,連接器阻止.
方法二:寫一個父類,將copy構造函數和copy賦值運算符函數聲明為private并且故意不實現,再繼承這個父類。
class A{
protected:
A(){}
~A(){}
private:
A(const A&);
A& operator=(const A&)
}
為多態基類聲明virtual析構函數
我的理解是:如果不這樣做,在delete父類的指針的時候無法使用多態性質,只會刪除掉父類的部分,而子類的部分會無法刪除,造成局部銷毀。
class A{
public:
A();
~A();
};
class B:public A{};
//如果設計一個函數 返回一個基類的指針 可以使用多態 來自動判斷是調用A還是B的析構來刪除對象
A* base_pointer=getPointer();
delete base_pointer;
//當子類用一個指向父類的指針來執行刪除,若父類的析構函數不是virtual,則無法調用子類的析構函數
//導致只刪除 子類中父類的部分 剩下子類獨有的部分
所以:無虛不基
base class的析構函數一定得是vritual,且可以推廣到其他函數,若一個class里面沒有一個virtual函數,那它不適合當一個base class。
class A{
public:
A();
virtual ~A();//應該這么搞
};
class B:public A{};
//如果設計一個函數 返回一個基類的指針 可以使用多態 來自動判斷是調用A還是B的析構來刪除對象
A* base_pointer=getPointer();
delete base_pointer;
但是,析構函數不能無端聲明為virtual,因為聲明為virtual需要虛表指針(vptr),vptr也是要占內存的,會增加對象的體積,減緩運行速度。
所以:當class至少含有一個virtual函數,才為它聲明vritual虛構函數
當然,也可以將虛構函數聲明為純虛函數,使該class成為一個抽象基類,注意:必須為這個純虛函數提供一份定義。因為析構函數是從派生類開始往基類調用,所以編譯器會在A的派生類的析構函數中調用~A()。
class A{
public:
virtual~ A()=0;
}
A::~A(){}//必須為這個純虛函數提供一份定義
別讓異常逃離析構函數
C++不喜歡析構函數出現異常。
可以在發生異常時終止程序,也可以吞下發生的異常
A::~A()
{
try{ a.close();}
catch(...){
...
//制作運轉記錄,記錄下close的失敗
std::abort();//終止程序
}
}
A::~A()
{
try{ a.close();}
catch(...){
...
//記錄下close的調用失敗
}
}
如果某個操作可能在失敗時拋出異常,而又必須要處理這個異常,這個異常必須來自析構以外的函數。(這里其實不太理解,文中給的例子是用一個新的成員函數來直行關閉)
絕不在構造和析構過程中調用virtual函數
我的理解是:在構造和析構的過程中調用的virtual成員函數并沒有多態性質(注意該虛函數不是指析構函數和構造函數是虛函數,而是除此之外的一個成員函數)
class A{
public:
A();
vritual void xxx() const =0;
};
A::A(){
...
xxx();
}
class B:public A{
public:
virtual xxx() const;
};
//當執行
B b;
派生類的base class成分會在派生類自身成分構造之前先構造,而A的構造函數調用了虛函數xxx,這時xxx是A的xxx,而不會多態調用B的xxx,即使目前是在創建B對象。
根本原因是:在派生類的基類構造期間,對象的類型是基類而不是派生類,只有當派生類自己的部分開始執行時,該對象才變成一個派生類。
該道理同樣用于析構函數
改法為:將A類的xxx改為non-vritual,在派生類的構造函數傳遞必要信息給基類的構造函數
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);
};
在構造期間,令派生類將必要的構造信息向上傳遞給基類的構造函數。
令operator=返回一個reference to *this
為了實現連鎖賦值,賦值操作符必須返回一個reference指向操作符的左側,這是為class實現賦值操作符時必須遵守的協議。
//連鎖賦值
int x,y,z;
x=y=z=1;
class A{
public:
A& operator=(const A& rhs)
{
return *this;
}
}
在operator=中處理自我賦值
自我賦值發生在對象賦值給自己本身,例如*px=*py;
px和py都指向一個對象,則是一個自我賦值。
常發生在用引用賦值,指針賦值,多態等
可能會引發delete時將賦值和被賦值對象都刪除了
避免方法:
1、證同測試
A &A operator=(const A& rhs){
if(this==&rhs) return *this;//一樣則什么也不做 直接返回
...
}
2、調整語序
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指向的一個副本,完成賦值
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;
}
復制對象時別忘了每個成分
如果自己定義了拷貝構造函數,編譯器將不會提醒你是否拷貝完所有成分,如果為class添加一個成員變量,則必須修改所有的copy函數和非標準形勢的operator=
給派生類寫copy函數的時候,也要復制它的基類的成分,那些成分往往是private,所以要讓派生類調用相應的base class函數
B::B(const B &rhs):A(rhs),//調用基類的copy構造
...//對派生類部分初始化
{}
B::operator=(const B& rhs){
A::operator=(rhs);//對基類部分賦值
...//對派生類部分賦值
return *this;
}
編寫copy函數時:
1、復制所有local成員變量
2、調用所有base classes內的適當copying函數
且不要嘗試以某個copy函數實現另一個copy函數
原文鏈接:https://blog.csdn.net/RolleX/article/details/126869128
相關推薦
- 2022-06-08 基于Apache?Hudi在Google云構建數據湖平臺的思路詳解_Linux
- 2022-04-18 Android?app本地切換logo和名稱_Android
- 2023-07-28 async await 寫法
- 2022-04-16 實例講解python讀取各種文件的方法_python
- 2022-08-11 python?scatter繪制散點圖_python
- 2022-11-14 mac 常用終端命令
- 2023-10-31 WebSocket消息推送
- 2022-08-10 Python進階學習修改閉包內使用的外部變量_python
- 最近更新
-
- 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同步修改后的遠程分支