C++自定義複製建構函式詳解

2020-07-16 10:04:41
程式設計師可以定義一個類的複製建構函式。程式設計師定義的複製建構函式必須有一個形參,它是對同一個類的參照。

因此,在前面的例子中,複製建構函式的原型就應該是下面這個樣子的:

NumberArray :: NumberArray(NumberArray&obj)

此複製建構函式通過在複製之前為新物件的指標分配單獨的記憶體來避免預設複製建構函式的問題:
NumberArray :: NumberArray (NumberArray &obj)
{
    arraySize = obj.arraySize;
    aPtr = new double[arraySize];
    for(int index = 0; index < arraySize; index++)
        aPtr[index] = obj.aPtr[index];
}
複製建構函式不應該改變被複製的物件,所以它將其實參宣告為 const。下面的程式展示了修改 NumberArray 類以使用程式設計師定義的複製建構函式的方法。類宣告在 NumberArray2.h 檔案中,其成員函數的實現在 NumberArray2.cpp 中給出。
//NumberArray2.h 的內容
#include <iostream>
using namespace std;

class NumberArray
{
    private:
        double *aPtr;
        int arraySize;
    public:
        NumberArray(NumberArray &);
        NumberArray(int size, double value);
        ~NumberArray() { if (arraySize > 0) delete [] aPtr;}
        void print() const;
        void.setValue(double value);
};

//NumberArray2. cpp 的內容
#include <iostream>
#include "NumberArray2.h"
using namespace std;
NumberArray::NumberArray(NumberArray &obj)
{
    arraySize = obj.arraySize;
    aPtr = new double[arraySize];
    for(int index = 0; index < arraySize; index++)
        aPtr[index] = obj.aPtr[index];
}
NumberArray::NumberArray(int size, double value)
{
    arraySize = size;
    aPtr = new double[arraySize];
    setValue(value);
}
void NumberArray::setValue(double value)
{
    for(int index = 0; index < arraySize; index++)
        aPtr[index] = value;
}
void NumberArray::print() const
{
    for (int index = 0; index < arraySize; index++)
        cout << aPtr[index] << " ";
}
//main函數的內容
// This program demonstrates the use of copy constructors.
#include <iostream>
#include <iomanip>
#include "NumberArray2.h"
using namespace std;
int main()
{
    NumberArray first(3, 10.5);
    //Make second a copy of first object
    NumberArray second = first;
    // Display the values of the two objects
    cout << setprecision(2) << fixed << showpoint;
    cout << "Value stored in first object is ";
    first.print();
    cout << "nValue stored in second object is ";
    second.print();
    cout << "nOnly the value in second object will " << "be changed.n";
    //Now change value stored in second object
    second.setValue(20.5);
    // Display the values stored in the two objects
    cout << "Value stored in first object is ";
    first.print ();
    cout << endl << "Value stored in second object is ";
    second.print ();
    return 0;
}
程式輸出結果:

Value stored in first object is 10.50 10.50 10.50
Value stored in second object is 10.50 10.50 10.50
Only the value in second object will be changed.
Value stored in first object is 10.50 10.50 10.50
Value stored in second object is 20.50 20.50 20.50

注意,複製建構函式必須具有一個形參,該形參是對同一個類的參照。如果忘記 & 識別符號參照形參將導致編譯器錯誤。此外,形參應該是一個 const 參照,因為複製建構函式不應該修改被複製的物件。

無論何時,只要在函數呼叫中按值傳遞物件,那麼編譯器就會自動呼叫複製建構函式,以建立物件的副本。正是出於這個原因,複製建構函式的形參必須按參照傳遞。因為,如果在建構函式被呼叫時按值傳遞,那麼建構函式將立即被再次呼叫以建立按值傳遞的副本,從而導致對建構函式呼叫的無休止鏈條。

複製建構函式的呼叫

每當建立一個物件,並使用同一個類的另一個物件來初始化它時,系統將自動呼叫複製建構函式,例如,以下每個初始化語句都將呼叫 Rectangle 類的複製建構函式:

Rectangle box(5, 10);
Rectangle b = box; // 初始化語句
Rectangle b1(box) ; // 初始化語句

當函數呼叫接收到某個類型別的形參值時,複製建構函式也會自動呼叫。例如,來看以下形式的函數:

void fun(Rectangle rect)
{
}

假設使用以下語句來呼叫它:

fun(box);

這將導致 Rectangle 複製建構函式被呼叫。最後,只要函數通過值返回類的物件,就會自動呼叫複製建構函式。因此,在以下函數中,當 return 語句被執行時將呼叫複製建構函式:
Rectangle makeRectangle()
{
    Rectangle rect(12, 3);
    return rect;
}
這是因為 return 語句必須為該物件建立一個臨時的非區域性副本,以便在函數返回之後,呼叫者可以使用該副本。總而言之,一個類的複製建構函式將在以下情況下被呼叫:
  • 變數被使用同一個類的物件初始化。
  • 函數被使用類的形參值呼叫。
  • 函數返回的值是一個類的物件。

注意,當按參照或指標傳遞類的形參時,複製建構函式不會被呼叫。而且,當函數返回類物件的參照或指標時,也不會呼叫複製建構函式。