天天看點

C++ 拷貝構造函數和重載指派運算符的差別

文章目錄

  • ​​拷貝構造函數​​
  • ​​重載指派運算符​​

指派運算符和拷貝構造函數最大差別是指派運算符沒有新的對象生成,而拷貝構造函數會生成新的對象。

為了更加形象 準确得描述 指派運算符和拷貝構造函數得差別,将詳細通過代碼展示兩者之間得差異。

拷貝構造函數

首先從構造函數說起,在C++面向對象的設計中,每一個對象代表一個抽象集合的實體,此時每一個實體在目前運作的程序中是需要對應的記憶體空間,即對象存在則有空間。為此,C++引入構造函數來執行個體化對象,并讓編譯器為該對象向作業系統申請對應的空間,進而能夠存在于作業系統之中。

拷貝構造函數則是為了将老對象的資料成員一一指派給新的對象資料成員的一種構造函數,即拷貝構造函數的結果和構造函數一緻,都是有新的對象生成。

檢視如下代碼:

#include <iostream>
using namespace std;

class A{
public:
    A(){ cout << "default constructor " << endl;  }
    A(int a) {num=a;cout << "constructor with param" << endl; }
    A(const A &a)
    {
        num=a.num;
        cout << "copy constructor " << endl;
    }
    ~A(){ cout << "destructor " << this->num << endl; }
    void print()
    {
        cout << this->num << endl;
    }

private:
    int num;

};
void para_copy(A &a)
{
}
int main()
{
    A a(100); // 重載構造函數
    A a1=a; //拷貝構造函數
    a1.print();
    A c;//預設構造函數
    return 0;
}      

輸出如下:

constructor with param //a 重載構造
copy constructor //a1 拷貝構造
100
default constructor //c預設構造
destructor 0 //c析構
destructor 100 //a1析構
destructor 100 //a析構      

拷貝構造函數調用場景如下:

  • 我們如上代碼中 一個對象由另一個對象來初始化
  • 對象作為函數參數
  • 對象作為函數傳回值

    關于對象作為函數參數以及傳回值時調用的拷貝構造函數,檢視代碼如下

#include <iostream>
using namespace std;

class A{
public:
    A(){ cout << "default constructor " << endl;  }
    A(int a) {num=a;cout << "constructor with param" << endl; }
    A(const A &a)
    {
        num=a.num;
        cout << "copy constructor " << endl;
    }
    ~A(){ cout << "destructor " << this->num << endl; }
    void print()
    {
        cout << this->num << endl;
    }

private:
    int num;

};
void param_copy(A a)
{
    cout << "param obj" << endl;
}
A return_value()
{
    A d(0);
    cout << "return obj" << endl;
    return d;
}
int main()
{
    A c;
    param_copy(c);
    return_value();
    return 0;
}      
  • 輸出如下
default constructor  //c 預設構造
copy constructor // 對象做參數,拷貝構造到臨時對象
param obj
destructor 0 // 析構掉臨時對象
constructor with param //函數return_value中 d 對象使用了重載構造函數
return obj 
destructor 0 //析構掉 d對象 ,局部變量
destructor 0 //析構掉 c對象      

關于深拷貝和淺拷貝的描述如下:

  1. 通常,預設生成的拷貝構造函數和指派運算符,隻是簡單的進行值的複制。如果類的資料成員有指針,僅僅通過值傳遞進行拷貝構造的話會造成兩個對象的成員指針指向同一塊記憶體,當兩個對象析構的時候會對同一個記憶體釋放兩次,進而會造成指針空懸。
  2. 深拷貝即以上第二種情況,資料成員中有指針變量的時候拷貝構造函數使用深拷貝,即構造函數中重新指定初始化對象的位址空間。

    代碼如下:

#include <iostream>
using namespace std;

class A{
public:
    A(){ p=new int(10);cout << "default constructor " << endl;  }
    //A(int a) {num=a;cout << "constructor with param" << endl; }
    A(const A &a)
    {
        num=a.num;
        p=new int(10);
        *p=*(a.p);
        cout << "copy constructor " << endl;
    }
    ~A(){ cout << "destructor " << this->num << endl; }
    void print()
    {
        if(p!=NULL)
        {
          delete p;
          cout << this->num << endl;
        }
    }

private:
    int num;
    int *p;

};
void param_copy(A a)
{
    cout << "param obj" << endl;
}
int main()
{
    A c;
    A b(c);
    return 0;
}      

重載指派運算符

#include <iostream>
using namespace std;

class Person
{
public:
    Person(){}
    Person(const Person& p)
    {
        cout << "Copy Constructor" << endl;
    }
 
    Person& operator=(const Person& p)
    {
        cout << "Assign" << endl;
        return *this;
    }
 
private:
    int age;
    string name;
};
 
void f(Person p)
{
    return;
}
 
Person f1()
{
    Person p;
    return p;
}
 
int main()
{
    Person p;
    Person p1 = p;    // A
    cout <<" p1 addr " <<  &p1 << endl;
    Person p2;
    cout <<" p2 addr " <<  &p2 << endl;
    p2 = p;           // B
    cout <<" p2 addr after asign " <<  &p2 << endl;
    f(p2);            // C f函數參數
    cout <<" p2 addr after asign " <<  &p2 << endl;
 
    p2 = f1();        // D f1傳回值為改對象,則先進行拷貝構造,在将傳回的對象指派給p2
    cout <<" p2 addr after asign " <<  &p2 << endl;
 
    Person p3 = f1(); // E 生成新對象,則為拷貝構造函數
    cout <<" p3 addr " <<  &p3 << endl;
 
    getchar();
    return 0;
}      
Copy Constructor
Assign
Copy Constructor
Copy Constructor
Assign
Copy Constructor      

繼續閱讀