複製構造函數與賦值函數

構造函數、析構函數、賦值函數是每一個類最基本的的函數。每一個類只有一個析構函數和一個賦值函數。可是有不少構造函數(一個爲複製構造函數,其餘爲普通構造函數。對於一個類A,若是不編寫上述四個函數,c++編譯器將自動爲A產生四個默認的函數,即:html

  • A(void)                                    //默認無參數構造函數
  • A(const A &a)                         //默認複製構造函數
  • ~A(void);                                //默認的析構函數
  • A & operator = (const A &a); //默認的賦值函數

既然能自動生成函數,爲何還須要自定義?緣由之一是「默認的複製構造函數」和"默認的賦值函數「均採用」位拷貝「而非」值拷貝「ios

位拷貝  v.s.  值拷貝c++

爲便於說明,以自定義String類爲例,先定義類,而不去實現。程序員

複製代碼
複製代碼
#include <iostream>
using namespace std;

class String  
{
    public:
        String(void);
        String(const String &other);
        ~String(void);
        String & operator =(const String &other);
    private:
 
char *m_data;
int val; };
複製代碼
複製代碼

位拷貝拷貝的是地址,而值拷貝拷貝的是內容。函數

若是定義兩個String對象a, b。當利用位拷貝時,a=b,其中的a.val=b.val;可是a.m_data=b.m_data就錯了:a.m_data和b.m_data指向同一個區域。這樣出現問題:post

  • a.m_data原來的內存區域未釋放,形成內存泄露
  • a.m_data和b.m_data指向同一塊區域,任何一方改變,會影響到另外一方
  • 當對象釋放時,b.m_data會釋放掉兩次

所以this

當類中還有指針變量時,複製構造函數和賦值函數就隱含了錯誤。此時須要本身定義。url

結論spa

  • 有一種特別常見的狀況須要本身定義複製控制函數:類具備指針哈函數。
  • 賦值操做符和複製構造函數能夠當作一個單元,當須要其中一個時,咱們幾乎也確定須要另外一個
  • 三法則:若是類須要析構函數,則它也須要賦值操做符和複製構造函數

注意指針

  • 若是沒定義複製構造函數(別的無論),編譯器會自動生成默認複製構造函數
  • 若是定義了其餘構造函數(包括複製構造函數),編譯器毫不會生成默認構造函數
  • 即便本身寫了析構函數,編譯器也會自動生成默認析構函數

所以此時若是寫String s是錯誤的,由於定義了其餘構造函數,就不會自動生成無參默認構造函數。

複製構造函數  v.s.  賦值函數

複製代碼
複製代碼
#include <iostream>
#include <cstring>
using namespace std;

class String  
{
    public:
        String(const char *str);
        String(const String &other);
        String & operator=(const String &other);
        ~String(void); 
    private:
        char *m_data;
};

String::String(const char *str)
{
    cout << "自定義構造函數" << endl;
    if (str == NULL)
    {
        m_data = new char[1];
        *m_data = '\0';
    }
    else
    {
        int length = strlen(str);
        m_data = new char[length + 1];
        strcpy(m_data, str);
    }
}

String::String(const String &other)
{
    cout << "自定義拷貝構造函數" << endl;
    int length = strlen(other.m_data);
    m_data = new char[length + 1];
    strcpy(m_data, other.m_data);
}

String & String::operator=(const String &other)
{
    cout << "自定義賦值函數" << endl; 

    if (this == &other)
    {
        return *this;
    }
    else
    {
        delete [] m_data;
        int length = strlen(other.m_data);
        m_data = new char[length + 1];
        strcpy(m_data, other.m_data);
        return *this;
    }
}

String::~String(void)
{
    cout << "自定義析構函數" << endl; 
    delete [] m_data;
}
int main()
{
    cout << "a(\"abc\")" << endl;
    String a("abc");

    cout << "b(\"cde\")" << endl;
    String b("cde");
    
    cout << " d = a" << endl;
    String d = a;

    cout << "c(b)" << endl;
    String c(b);

    cout << "c = a" << endl;
    c = a;

    cout << endl;
}
複製代碼
複製代碼

執行結果

說明幾點

1. 賦值函數中,上來比較 this == &other 是很必要的,由於防止自複製,這是很危險的,由於下面有delete []m_data,若是提早把m_data給釋放了,指針已成野指針,再賦值就錯了

2. 賦值函數中,接着要釋放掉m_data,不然就沒機會了(下邊又有新指向了)

3. 拷貝構造函數是對象被建立時調用,賦值函數只能被已經存在了的對象調用

    注意:String a("hello"); String b("world");  調用自定義構造函數

             String c=a;調用拷貝構造函數,由於c一開始不存在,最好寫成String c(a);

 

 

C++賦值運算符函數

爲類添加賦值運算符函數:

類型定義

複製代碼
class CMyString { public: CMyString(char *pData = NULL); CMyString(const CMyString &str); ~CMyString(void); CMyString &operator=(const CMyString &); private: char *m_pData; };
複製代碼

要點:

一、返回值類型爲該類型的引用,並在函數結束前返回實例自身的引用(即 *this);

二、是否把傳入的參數聲明爲常量引用【const CmyString &str】;

三、是否釋放自身已有內存,不然會形成「內存泄漏」;

四、是否判斷參數與當前示例是指向的同一個對象;

解法:

複製代碼
/* 適用於初級C++程序員的解法 */ CMyString &CMyString::operator=(const CMyString &str) { //首先檢測兩個指針是否指向同一個對象 if (this == &str) return *this; //釋放原內存  delete []m_pData; m_pData = NULL; //從新申請內存 m_pData = new char[strlen(str.m_pData) + 1]; strcpy(m_pData,str.m_pData); //謹記:返回*this return *this; } /* 適用於高級C++程序員的解法 */ CMyString &CMyString::operator=(const CMyString &str) { if (this != &str) //確保不指向同一個實例  { CMyString strTemp(str); char *pTemp = strTemp.m_pData; //指針指向須要更換的對象 //strTemp.m_pData指向原來的對象,確保內存不足時能夠找到原來對象的值 strTemp.m_pData = m_pData; m_pData = pTemp; //更換原對象的值 } //自動調用strTemp的析構函數,銷燬strTemp對象並回收pTemp的內存 return *this; }
相關文章
相關標籤/搜索