C++ : 构造函数,拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符应用场景
??構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),移動(dòng)構(gòu)造函數(shù),拷貝賦值運(yùn)算符,移動(dòng)賦值運(yùn)算符應(yīng)用場景
#include <iostream> using namespace std;class ConstructTest{ public:ConstructTest(){cout<<"default Constructor\n";dim_=0;base_= nullptr;}; ~ConstructTest(){cout<<"Destructor:base "<<base_<<endl;if (base_ != nullptr){delete base_;}}ConstructTest(int dim){cout<<"Constructor with param"<<endl;dim_=dim;base_ = new int [dim];for (int i = 0; i < dim_; ++i) {*(base_ + i) = 0;}}ConstructTest (const ConstructTest & a){cout<<"copy Constructor"<<endl;dim_= a.dim_;base_ = new int [dim_];for (int i = 0; i < dim_; ++i) {*(base_ + i) = *(a.base_+i);}}ConstructTest& operator =(const ConstructTest & a){cout<<"copy assign "<<endl;dim_= a.dim_;base_ = new int [dim_];for (int i = 0; i < dim_; ++i) {*(base_ + i) = *(a.base_+i);}return *this;}ConstructTest& operator =( ConstructTest && a)noexcept{cout<<"moving copy assign "<<endl;//避免自己移動(dòng)自己if ( this == &a )return *this;delete base_;dim_ = a.dim_;base_ = a.base_;a.base_ = nullptr;return *this;}ConstructTest (ConstructTest && a) noexcept{cout<<"moving copy Constructor"<<endl;dim_ = a.dim_;base_ = a.base_;a.base_ = nullptr;}public:int dim_;int * base_; }; ConstructTest square(ConstructTest para){ConstructTest ret(para.dim_);ret.base_ = new int [para.dim_];for (int i = 0; i < para.dim_; ++i) {*(ret.base_+i) = *(para.base_+i) * (*(para.base_+i));}return ret; }int main(){ConstructTest c1(3);ConstructTest c2(c1);ConstructTest c4 ;c4=c2;cout<<"------------------------\n";ConstructTest c5 ;c5=square(c4);ConstructTest c6 = std::move(c5);cout<<"------------------------\n";ConstructTest c7;c7=ConstructTest();ConstructTest c8 = square(c7);ConstructTest c9 = ConstructTest();cout<<"<<<<<<finish >>>>>>>>\n"; }C++ 和JAVA不一樣的是,C++ 區(qū)分了值類型和引用類型,不像JAVA一樣全是引用類型。創(chuàng)建對象 JAVA 用? <類名> 對象名= new ......? 而C++ 用 <類名> 對象名 就行 . 對于普通對象 用值傳遞的方式傳到形參,有一個(gè)隱式賦值的過程,此時(shí)調(diào)用的拷貝構(gòu)造函數(shù).以下的構(gòu)造函數(shù)使用場景:
構(gòu)造函數(shù)?: 創(chuàng)建對象時(shí),給對象初始化時(shí)調(diào)用。
拷貝(復(fù)制)構(gòu)造函數(shù): 利用相同的類對象給新對象初始化.時(shí)調(diào)用.
拷貝賦值運(yùn)算符 : 兩個(gè)舊對象之間的賦值。
所謂“移動(dòng)”就是把a(bǔ)的內(nèi)存資源挪為自用。
移動(dòng)構(gòu)造函數(shù): 在創(chuàng)建對象時(shí),用臨時(shí)對象初始化時(shí)調(diào)用。在返回值傳給返回值的副本時(shí)也會(huì)調(diào)用。
移動(dòng)賦值運(yùn)算符:?用臨時(shí)對象給舊對象賦值時(shí)調(diào)用。
?
我用的是CLION,在CMakeList.txt 加入如下代碼,來取消編譯器優(yōu)化
add_compile_options(-fno-elide-constructors)在無編譯器優(yōu)化的情況下,輸出結(jié)果:
Constructor with param copy Constructor default Constructor copy assign ------------------------ default Constructor copy Constructor Constructor with param moving copy Constructor Destructor:base 0 moving copy assign Destructor:base 0 Destructor:base 0x3e1da8 ------------------------ moving copy Constructor ------------------------ default Constructor default Constructor moving copy assign Destructor:base 0 copy Constructor Constructor with param moving copy Constructor Destructor:base 0 moving copy Constructor Destructor:base 0 Destructor:base 0x3e1da8 default Constructor moving copy Constructor Destructor:base 0 <<<<<<finish >>>>>>>> Destructor:base 0 Destructor:base 0x3e1918 Destructor:base 0 Destructor:base 0x3e1dd8 Destructor:base 0 Destructor:base 0x3e1d90 Destructor:base 0x3e1d78 Destructor:base 0x3e1d60Process finished with exit code 0在main函數(shù)第一到第四行中,展示了構(gòu)造函數(shù),拷貝構(gòu)造,和拷貝賦值。
從第5行起:
cout<<"------------------------\n";ConstructTest c5 ;c5=square(c4);ConstructTest c6 = std::move(c5);cout<<"------------------------\n";首先用 默認(rèn)構(gòu)造函數(shù)創(chuàng)建對象c5,
在c5 = square(c4) 中, 首先把實(shí)參c4賦值給square的形參param,此時(shí)用的是拷貝構(gòu)造函數(shù)。
在square函數(shù)體中,創(chuàng)建了局部對象ret,此時(shí)調(diào)用構(gòu)造函數(shù),
然后為返回值ret創(chuàng)建副本,此時(shí)調(diào)用移動(dòng)構(gòu)造函數(shù),接著函數(shù)體結(jié)束,把形參param析構(gòu),
然后把原來ret的副本賦值給c5 ,對過程是對已經(jīng)存在的對象c5進(jìn)行賦值,所以調(diào)用移動(dòng)賦值。
接著把ret 和ret的副本給析構(gòu)。
利用 std::move() 手動(dòng)把c5的內(nèi)容移動(dòng)給某個(gè)臨時(shí)引用,把臨時(shí)引用初始化給c6,調(diào)用移動(dòng)構(gòu)造函數(shù).
cout<<"------------------------\n";ConstructTest c7;c7=ConstructTest();ConstructTest c8 = square(c7);ConstructTest c9 = ConstructTest();cout<<"<<<<<<finish >>>>>>>>\n";先默認(rèn)構(gòu)造函數(shù)創(chuàng)建c7對象。
用默認(rèn)構(gòu)造函數(shù)創(chuàng)建臨時(shí)對象,把臨時(shí)對象"移動(dòng)"給c7;移動(dòng)完后把臨時(shí)對象析構(gòu)
在c8?= square(c7) 中, 首先把實(shí)參c7賦值給square的形參param,此時(shí)用的是拷貝構(gòu)造函數(shù)。
在square函數(shù)體中,創(chuàng)建了局部對象ret,此時(shí)調(diào)用構(gòu)造函數(shù),
然后為返回值ret創(chuàng)建副本,此時(shí)調(diào)用移動(dòng)構(gòu)造函數(shù),接著函數(shù)體結(jié)束,把形參param析構(gòu),
然后把原來ret的副本賦值給c8 ,對還未創(chuàng)建的對象的對象c8進(jìn)行初始化,所以調(diào)用移動(dòng)構(gòu)造。
接著把ret 和ret的副本給析構(gòu)。
創(chuàng)建臨時(shí)對象,并用給c9進(jìn)行初始化,最后把臨時(shí)對象析構(gòu)掉;
程序結(jié)束,析構(gòu)所有變量。
編程思路
? ? 在類成員變量帶有指針的情況下,會(huì)面臨如何施放資源的難題。應(yīng)為默認(rèn)拷貝構(gòu)造,拷貝賦值,移動(dòng)構(gòu)造、移動(dòng)賦值只是單單的淺拷貝。即值復(fù)制 指針指向內(nèi)存的地址。在淺拷貝后,就會(huì)有兩個(gè)對象中的成員指針指向同一處內(nèi)存空間,如果在析構(gòu)函數(shù)中對成員指針delete,最后就會(huì)面臨對同一處內(nèi)存空間施放兩次。在C++中,如果訪問不屬于本程序中的內(nèi)存就會(huì)出現(xiàn)“段錯(cuò)誤”,即指針越界.?
? ? 在編程時(shí),可以利用 boost::shared_ptr 來管理動(dòng)態(tài)分配的內(nèi)存,此時(shí)可以不用理睬資源施放問題。因?yàn)橹悄苤羔樐軌驅(qū)?dòng)態(tài)內(nèi)存的引用計(jì)數(shù)歸零時(shí)自動(dòng)清理內(nèi)存空間。
? ?如果要用 普通指針來管理動(dòng)態(tài)內(nèi)存,那么就要考慮施放資源,淺拷貝與深拷貝的問題。如果想用深拷貝那么就要,寫好構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),拷貝賦值運(yùn)算符,移動(dòng)構(gòu)造,移動(dòng)賦值運(yùn)算符;
? 在構(gòu)造函數(shù)中初始化所有成員變量包括指針;在拷貝構(gòu)造和拷貝賦值中重新為指針分配內(nèi)存空間,并把對應(yīng)的內(nèi)存值進(jìn)行賦值;在移動(dòng)構(gòu)造和移動(dòng)賦值中,把返回內(nèi)容復(fù)制完后要把臨時(shí)對象指針內(nèi)容重新賦空,達(dá)到不拷貝內(nèi)存又不會(huì)施放兩次內(nèi)存的目的,即所謂的"移動(dòng)“.
總結(jié)
以上是生活随笔為你收集整理的C++ : 构造函数,拷贝构造函数,移动构造函数,拷贝赋值运算符,移动赋值运算符应用场景的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ :Signal: SIGSEGV
- 下一篇: C++ 数据结构-图相关操作的算法思路