C++構(gòu)造析構(gòu)賦值運(yùn)算函數(shù)應(yīng)用詳解
了解C++默默編寫哪些函數(shù)
當(dāng)實(shí)現(xiàn)一個空類,c++會為你補(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)用是,才會被編譯器創(chuàng)建出來。如果你自己聲明了一個構(gòu)造函數(shù),編譯器將不會創(chuàng)建默認(rèn)構(gòu)造函數(shù)。
當(dāng)然,編譯器有時會拒絕生成operator=
class A{ public: NameObject{string& name}; private: string& nameValue;//為一個字符串引用 } string newDog("peter"); string oldDog("fx") A a(newDog); A b(olddog); a=b;//此處賦值就會導(dǎo)致a的nameValue綁定到不同對對象上面 而引用一旦綁定無法更改 是錯誤的
所以如果打算在一個含有引用成員的class中支持賦值操作,必須要自己定義operator=()
不想使用編譯器函數(shù)
若不想使用編譯器自動生成的函數(shù),就該明確拒絕
如果一個類你想讓它是獨(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。聲明一個成員函數(shù),阻止了編譯器自己創(chuàng)建它,而聲明為private,又可以阻止別人調(diào)用它。
客戶試圖拷貝A對象時,編譯器會阻止,而當(dāng)在member或friend函數(shù)之內(nèi)調(diào)用時,連接器阻止.
方法二:寫一個父類,將copy構(gòu)造函數(shù)和copy賦值運(yùn)算符函數(shù)聲明為private并且故意不實(shí)現(xiàn),再繼承這個父類。
class A{ protected: A(){} ~A(){} private: A(const A&); A& operator=(const A&) }
為多態(tài)基類聲明virtual析構(gòu)函數(shù)
我的理解是:如果不這樣做,在delete父類的指針的時候無法使用多態(tài)性質(zhì),只會刪除掉父類的部分,而子類的部分會無法刪除,造成局部銷毀。
class A{ public: A(); ~A(); }; class B:public A{}; //如果設(shè)計(jì)一個函數(shù) 返回一個基類的指針 可以使用多態(tài) 來自動判斷是調(diào)用A還是B的析構(gòu)來刪除對象 A* base_pointer=getPointer(); delete base_pointer; //當(dāng)子類用一個指向父類的指針來執(zhí)行刪除,若父類的析構(gòu)函數(shù)不是virtual,則無法調(diào)用子類的析構(gòu)函數(shù) //導(dǎo)致只刪除 子類中父類的部分 剩下子類獨(dú)有的部分
所以:無虛不基
base class的析構(gòu)函數(shù)一定得是vritual,且可以推廣到其他函數(shù),若一個class里面沒有一個virtual函數(shù),那它不適合當(dāng)一個base class。
class A{ public: A(); virtual ~A();//應(yīng)該這么搞 }; class B:public A{}; //如果設(shè)計(jì)一個函數(shù) 返回一個基類的指針 可以使用多態(tài) 來自動判斷是調(diào)用A還是B的析構(gòu)來刪除對象 A* base_pointer=getPointer(); delete base_pointer;
但是,析構(gòu)函數(shù)不能無端聲明為virtual,因?yàn)槁暶鳛関irtual需要虛表指針(vptr),vptr也是要占內(nèi)存的,會增加對象的體積,減緩運(yùn)行速度。
所以:當(dāng)class至少含有一個virtual函數(shù),才為它聲明vritual虛構(gòu)函數(shù)
當(dāng)然,也可以將虛構(gòu)函數(shù)聲明為純虛函數(shù),使該class成為一個抽象基類,注意:必須為這個純虛函數(shù)提供一份定義。因?yàn)槲鰳?gòu)函數(shù)是從派生類開始往基類調(diào)用,所以編譯器會在A的派生類的析構(gòu)函數(shù)中調(diào)用~A()。
class A{ public: virtual~ A()=0; } A::~A(){}//必須為這個純虛函數(shù)提供一份定義
別讓異常逃離析構(gòu)函數(shù)
C++不喜歡析構(gòu)函數(shù)出現(xiàn)異常。
可以在發(fā)生異常時終止程序,也可以吞下發(fā)生的異常
A::~A() { try{ a.close();} catch(...){ ... //制作運(yùn)轉(zhuǎn)記錄,記錄下close的失敗 std::abort();//終止程序 } } A::~A() { try{ a.close();} catch(...){ ... //記錄下close的調(diào)用失敗 } }
如果某個操作可能在失敗時拋出異常,而又必須要處理這個異常,這個異常必須來自析構(gòu)以外的函數(shù)。(這里其實(shí)不太理解,文中給的例子是用一個新的成員函數(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ù),而是除此之外的一個成員函數(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成分會在派生類自身成分構(gòu)造之前先構(gòu)造,而A的構(gòu)造函數(shù)調(diào)用了虛函數(shù)xxx,這時xxx是A的xxx,而不會多態(tài)調(diào)用B的xxx,即使目前是在創(chuàng)建B對象。
根本原因是:在派生類的基類構(gòu)造期間,對象的類型是基類而不是派生類,只有當(dāng)派生類自己的部分開始執(zhí)行時,該對象才變成一個派生類。
該道理同樣用于析構(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=返回一個reference to *this
為了實(shí)現(xiàn)連鎖賦值,賦值操作符必須返回一個reference指向操作符的左側(cè),這是為class實(shí)現(xiàn)賦值操作符時必須遵守的協(xié)議。
//連鎖賦值 int x,y,z; x=y=z=1; class A{ public: A& operator=(const A& rhs) { return *this; } }
在operator=中處理自我賦值
自我賦值發(fā)生在對象賦值給自己本身,例如*px=*py;
px和py都指向一個對象,則是一個自我賦值。
常發(fā)生在用引用賦值,指針賦值,多態(tài)等
可能會引發(fā)delete時將賦值和被賦值對象都刪除了
避免方法:
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指向的一個副本,完成賦值 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ù)制對象時別忘了每個成分
如果自己定義了拷貝構(gòu)造函數(shù),編譯器將不會提醒你是否拷貝完所有成分,如果為class添加一個成員變量,則必須修改所有的copy函數(shù)和非標(biāo)準(zhǔn)形勢的operator=
給派生類寫copy函數(shù)的時候,也要復(fù)制它的基類的成分,那些成分往往是private,所以要讓派生類調(diào)用相應(yīng)的base class函數(shù)
B::B(const B &rhs):A(rhs),//調(diào)用基類的copy構(gòu)造 ...//對派生類部分初始化 {} B::operator=(const B& rhs){ A::operator=(rhs);//對基類部分賦值 ...//對派生類部分賦值 return *this; }
編寫copy函數(shù)時:
1、復(fù)制所有l(wèi)ocal成員變量
2、調(diào)用所有base classes內(nèi)的適當(dāng)copying函數(shù)
且不要嘗試以某個copy函數(shù)實(shí)現(xiàn)另一個copy函數(shù)
到此這篇關(guān)于C++構(gòu)造析構(gòu)賦值運(yùn)算函數(shù)應(yīng)用詳解的文章就介紹到這了,更多相關(guān)C++構(gòu)造析構(gòu)賦值運(yùn)算內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++淺析構(gòu)造函數(shù)的特性
- C++類與對象深入之構(gòu)造函數(shù)與析構(gòu)函數(shù)詳解
- C++超詳細(xì)講解構(gòu)造函數(shù)與析構(gòu)函數(shù)的用法及實(shí)現(xiàn)
- C++分析類的對象作類成員調(diào)用構(gòu)造與析構(gòu)函數(shù)及靜態(tài)成員
- C++分析構(gòu)造函數(shù)與析造函數(shù)的特點(diǎn)梳理
- 一起來學(xué)習(xí)C++的構(gòu)造和析構(gòu)
- C++繼承中的對象構(gòu)造與析構(gòu)和賦值重載詳解
- C++編程析構(gòu)函數(shù)拷貝構(gòu)造函數(shù)使用示例詳解
- C++類的構(gòu)造與析構(gòu)特點(diǎn)及作用詳解
相關(guān)文章
C/C++?實(shí)現(xiàn)動態(tài)資源文件釋放的方法
當(dāng)我們開發(fā)Windows應(yīng)用程序時,通常會涉及到使用資源(Resource)的情況。資源可以包括圖標(biāo)、位圖、字符串等,它們以二進(jìn)制形式嵌入到可執(zhí)行文件中,這篇文章主要介紹了C/C++?實(shí)現(xiàn)動態(tài)資源文件釋放,需要的朋友可以參考下2023-12-12美化你的代碼 vb(VBS)代碼格式化的實(shí)現(xiàn)代碼
雖然VB.NET出現(xiàn)很久了,但還有好多人仍然在使用VB6。我在實(shí)現(xiàn)一些小功能的時候也喜歡用VB6,畢竟誰都不想每天的美好心情被VS那烏龜般的啟動速度影響2012-05-05Qt自定義控件實(shí)現(xiàn)進(jìn)度儀表盤
這篇文章主要介紹了Qt自定義控件實(shí)現(xiàn)進(jìn)度儀表盤,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-12-12C語言中g(shù)etch()函數(shù)詳解及簡單實(shí)例
這篇文章主要介紹了C語言中g(shù)etch()函數(shù)詳解及簡單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03Matlab利用垂距法實(shí)現(xiàn)提取離散坐標(biāo)數(shù)據(jù)特征點(diǎn)
垂距法是指根據(jù)中間頂點(diǎn)到其前、后兩相鄰頂點(diǎn)連線的距離的大小,來確定是否保留該頂點(diǎn)的一種線要素頂點(diǎn)壓縮算法。本文將利用這一算法實(shí)現(xiàn)提取離散坐標(biāo)數(shù)據(jù)特征點(diǎn),需要的可以參考下2022-04-04