C++複製構造函數以及賦值操做符

當定義一個新類型的時候,須要顯式或隱式地指定複製、賦值和撤銷該類型的對象時會發生什麼——這是經過定義特殊成員:複製構造函數、賦值操做符和析構函數來達到的。若是沒有顯式定義複製構造函數或者賦值操做符,編譯器(一般)會爲咱們定義。 函數

看看編譯器默認的構造函數和賦值操做符作了些什麼工做: 優化

class_a.h this

#ifndef _CLASS_A_H
#define _CLASS_A_H
class A { 
public:
        A();    
        A(int a); 
private:
        int a;  
};
#endif

class_a.cpp spa

#include "class_a.h"
A::A(): a(0) 
{
} 
A::A(int a)
{
        this->a = a;
}

main.cpp 操作系統

#include "class_a.h"

int main()
{
        A obj1;         //調用無形參構造函數
        A obj2(2);      //形參爲int的構造函數
        A obj3(obj1);   //默認複製構造函數
        A obj4 = 3;     //調用形參爲int的構造函數產生類A的臨時變量,調用默認賦值操做符進行賦值
        A obj5 = obj1;  //調用默認賦值操做符

        return 0;
}

編譯執行g++ -g main.cpp class_a.cpp
反彙編objdump -S a.out
可知:
默認複製構造函數執行的實質將obj1的內存複製到obj3的內存上
默認複製操做符當形參爲非A&時,調用相應形參的構造函數(編譯器層面上並無生成臨時變量的內存,應該是編譯器優化掉了;生成臨時變量是C++語法層面上的);不然直接將對象的內存複製到新對象上 指針

原來默認的複製構造函數以及默認的賦值操做符只是複製sizeof(A)大小的內存複製給新的對象,或者是調用了相應的單形參的的構造函數;這也就是咱們常說的淺拷貝,當咱們的類中有指針或者引用時,必定要顯式的定義複製構造函數以及賦值操做符。 code

咱們顯式的定義了複製構造函數以及默認賦值操做符: 對象

class_a.h 進程

#ifndef _CLASS_A_H
#define _CLASS_A_H
class A { 
public:
        A();    
        A(int a); 
        A(const A& obj_a); 
        A& operator=(const A& obj_a); 
        ~A();   
private:
        int a;  
};
#endif

class_a.cpp 內存

#include "class_a.h"

A::A(): a(0)
{
} 

A::A(int a)
{
        this->a = a;
}

A::A(const A& obj_a)
{
        this->a = obj_a.a;
}

A& A::operator=(const A& obj_a)
{
        this->a = obj_a.a;
        return *this;
}

A::~A()
{
}

main.cpp

#include "class_a.h"

int main()
{
        A obj1;           //調用無形參構造函數
        A obj2(2);        //調用形參爲int的構造函數
        A obj3(obj1);     //調用複製構造函數
        A obj4 = 3;       //調用形參爲int的構造函數產生臨時變量(若設爲explicit編譯不過)
                          //調用複製操做符進行賦值
        A obj5 = A(4);    //調用形參爲int的構造函數產生臨時變量(若設爲explicit編譯照樣經過)
                          //調用複製操做符進行賦值
        A obj6 = obj1;    //調用複製構造函數產生臨時變量,調用複製操做符進行賦值
        A obj7 = A(obj1); //調用複製構造函數產生臨時變量,調用複製操做符進行賦值

        return 0;
}

編譯 g++ -g main.cpp class_a.cpp
反彙編objdump -S a.out
可知:
obj4並無產生臨時變量,生成過程跟obj2同樣,調用形參爲int的構造函數
obj5也並無產生臨時變量,生成過程跟obj2同樣,調用形參爲int的構造函數
obj6也沒有產生臨時變量,直接調用自定義複製構造函數
obj7也沒有產生臨時變量,也是直接調用自定義複製構造函數

看來產生臨時變量是C++語法上的,在編譯器上進行了優化,不然產生臨時變量的效率比較低,先要調用構造函數,而後還要調用析構函數進行析構,並且上述代碼也只是調用了7次構造函數,驗證了在編譯器上沒有產生臨時變量。

C++ primer說:由於用於向函數產地對象和從函數返回對象,該構造函數通常不該該設置爲explicit

將上述代碼的複製構造函數設置成explicit,編譯報錯:obj4, obj5, obj7 :no matching function for call to ‘A::A(A)’obj6: no matching function for call to ‘A::A(&A)’,能夠猜想C++語法上的臨時變量是經過A::A(A)和A::A(A&)產生的,由於沒有定義A::A(A)的構造函數,而A::A(A&)被定義成了explicit抑制了臨時變量的產生。

複製構造函數以及賦值操做符的自定義,能夠解決淺拷貝的問題;不會致使因爲對象的析構中釋放new對象致使其餘對象的影響。
可是咱們仍是得注意另外一個問題,當對象中帶有操做系統進程級別的一些參數時(好比fd),在函數調用的時候不能在函數內部定義一個臨時變量,並將對象賦值給這個臨時變量,由於臨時變量析構時就會調用自定義的close(fd),這樣就會致使意想不到的結果,這種狀況下,咱們須要定義一個該對象的指針而不是一個臨時變量對象。

相關文章
相關標籤/搜索