C++自定义复制构造函数详解

程序员可以定义一个类的复制构造函数。程序员定义的复制构造函数必须有一个形参,它是对同一个类的引用。

因此,在前面的例子中,复制构造函数的原型就应该是下面这个样子的:

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 语句必须为该对象创建一个临时的非局部副本,以便在函数返回之后,调用者可以使用该副本。总而言之,一个类的复制构造函数将在以下情况下被调用:
  • 变量被使用同一个类的对象初始化。
  • 函数被使用类的形参值调用。
  • 函数返回的值是一个类的对象。

注意,当按引用或指针传递类的形参时,复制构造函数不会被调用。而且,当函数返回类对象的引用或指针时,也不会调用复制构造函数。