1. 程式人生 > >C++ 深入討論建構函式

C++ 深入討論建構函式

1、深複製和淺複製是什麼? 使用編譯器提供的預設建構函式完成的複製,稱為淺複製。深複製指源頭物件和複製物件相互獨立,其中任何一個物件的改動都不會對另外一個物件造成影響,即在類中定義一個建構函式,不再使用系統預設提供的建構函式。2、淺拷貝的隱患?

如果一個類擁有資源,當這個類的物件發生複製過程的時候,資源重新分配,這個過程就是深拷貝

之前提到,如果沒有自定義複製建構函式,則系統會建立預設的複製建構函式,但系統建立的預設複製建構函式只會執行“淺拷貝”,即將被拷貝物件的資料成員的值一一賦值給新建立的物件,若該類的資料成員中有指標成員,則會使得新的物件的指標所指向的地址與被拷貝物件的指標所指向的地址相同,delete該指標時則會導致兩次重複delete而出錯。下面是示例:

 預設建構函式存在這使用的隱患,示例如下: 

#include <iostream.h>
#include <string.h>
class Person 
{
public :
         
    // 建構函式
    Person(char * pN)
    {
        cout << "一般建構函式被呼叫 !\n";
        m_pName = new char[strlen(pN) + 1];
        //在堆中開闢一個記憶體塊存放pN所指的字串
        if(m_pName != NULL) 
        {
           //如果m_pName不是空指標,則把形參指標pN所指的字串複製給它
             strcpy(m_pName ,pN);
        }
    }        
       
    // 系統建立的預設複製建構函式,只做位模式拷貝
    Person(Person & p)    
    { 
        //使兩個字串指標指向同一地址位置         
        m_pName = p.m_pName;         
    }
 
    ~Person( )
    {
        delete m_pName;
    }
         
private :
    char * m_pName;
};
 
void main( )
{ 
    Person man("lujun");
    Person woman(man); 
     
    // 結果導致   man 和    woman 的指標都指向了同一個地址
     
    // 函式結束析構時
    // 同一個地址被delete兩次
}
 
 
// 下面自己設計複製建構函式,實現“深拷貝”,即不讓指標指向同一地址,而是重新申請一塊記憶體給新的物件的指標資料成員
Person(Person & chs);
{
     // 用運算子new為新物件的指標資料成員分配空間
     m_pName=new char[strlen(p.m_pName)+ 1];
 
     if(m_pName)         
     {
             // 複製內容
            strcpy(m_pName ,chs.m_pName);
     }
   
    // 則新建立的物件的m_pName與原物件chs的m_pName不再指向同一地址了
}
  要修復錯誤,要採用深複製,使用複製建構函式,其標準宣告如下:      demo(const demo &a);3、複製建構函式幾個原則:C++ primer p406 :複製建構函式是一種特殊的建構函式,具有單個形參,該形參(常用const修飾)是對該類型別的引用。當定義一個新物件並用一個同類型的物件對它進行初始化時,將顯示使用複製建構函式。當該型別的物件傳遞給函式或從函式返回該型別的物件時,將隱式呼叫複製建構函式。C++支援兩種初始化形式:複製初始化(int a = 5;)和直接初始化(int a(5);)對於其他型別沒有什麼區別,對於類型別直接初始化直接呼叫實參匹配的建構函式,複製初始化總是呼叫複製建構函式,也就是說:A x(2);  //直接初始化,呼叫建構函式A y = x;  //複製初始化,呼叫複製建構函式必須定義複製建構函式的情況:只包含類型別成員或內建型別(但不是指標型別)成員的類,無須顯式地定義複製建構函式也可以複製;有的類有一個資料成員是指標
,或者是有成員表示在建構函式中分配的其他資源,這兩種情況下都必須定義複製建構函式。什麼情況使用複製建構函式:類的物件需要拷貝時,拷貝建構函式將會被呼叫。以下情況都會呼叫拷貝建構函式:(1)一個物件以值傳遞的方式傳入函式體 (2)一個物件以值傳遞的方式從函式返回 (3)一個物件需要通過另外一個物件進行初始化。4、複製賦值運算子函式     如果類中沒有定義複製賦值運算的函式,編譯器會提供預設複製賦值運算子函式,預設複製賦值運算函式對於基本型別的成員變數,按位元組複製,對於類型別的成員變數,呼叫其對應型別的複製賦值運算子函式。     通過定義operate=的函式,可以對賦值進行定義。像其他任何函式一樣,操作符函式有一個返回值和形參表。形參表必須具有與該操作符運算元書目相同的形參(如果操作符是一個成員,則包括隱式this形參)。賦值是二元運算,所以該操作符函式有兩個形參:第一個形參(隱含的this指標)對應著左運算元,第二個形參對應右運算元。 一個應用了對賦值號過載的拷貝建構函式的例子:
#include <iostream>

using namespace std;

class A
{
public:
    A(int);//建構函式
    A(const A &);//拷貝建構函式
    ~A();
    void print();
    int *point;
    A &operator=(const A &);
};

A::A(int p)
{
    point = new int;
    *point = p;
}

A::A(const A &b)
{
    *this = b;
    cout<<"呼叫拷貝建構函式"<<endl;
}

A::~A()
{
    delete point;
}

void A::print()
{
    cout<<"Address:"<<point<<" value:"<<*point<<endl;
}

A &A::operator=(const A &b)
{
    if( this != &b)
    {
        delete point;
        point = new int;
        *point = *b.point;
    }
}


int main()
{
    A x(2);
    A y = x;
    x.print();
    delete x.point;
    y.print();

    return 0;
}