C++中为什么不允许复制构造函数传值参数呢?如果采用传值方式定义复制构造函数会发生什么呢?下面进行解答。
关于下面那程序执行时出现编译错误的问题,程序来源《剑指offer》。
- 代码1
class A
{
public:
A(int n) { value = n; }
A(A other) { value = other.value; }
void printA() {
cout << "value = " << value << endl;
}
private:
int value;
};
int main(void) {
A a1 = 10;
A a2 = a1;
a2.printA();
system("pause");
return 0;
}
按照代码执行顺序分析原因:
- 问题出在
A a2 = a1;
这句代码中,在这句代码执行时,根据c++特性,会调用类A的拷贝构造函数(关于拷贝构造函数,当没有显式定义时会有默认的拷贝构造函数,且以浅拷贝的方式实现的)。类A中定义了拷贝构造函数A(A other) { value = other.value; }
,则调用此段代码。 - 当程序执行到拷贝函数时,由于类A的拷贝构造函数采用了值传递的方式,对于值传递,当实参a1传入时,会在栈中创建一个a1的复制体(也相当于一个中间变量),假设此变量名为temp1,这样拷贝构造函数中的语句
value = other.value;
可以看做类似于a1.value = temp1.value;
,通过复制体temp1完成对实例a1中value属性的赋值。 - 问题的关键来了,这个复制体是如何得到的呢?答案是这个复制体是由类A的拷贝构造函数生成的,也就是类似于执行了
A temp1 = a1;
这样一段语句,生成temp1存于栈中。
注: 关于这个复制体是否是由A的拷贝构造函数生成的可以看下面的代码2,类A的构造函数中有一个是对类B的拷贝,也采用了值传递的方式,可以在运行结果中看到在调用这个构造函数时会执行类B的拷贝构造函数。 - 这时再看3中
A temp1 = a1;
语句,是否感觉似曾相识,看1中A a2 = a1;
你会发现它们是几乎一样的。这样在生成temp1的过程中和生成a2的过程类似,也需要调用类A的拷贝构造函数,在执行拷贝构造函数的过程中也会生成中间变量temp2,执行A temp2 = a1;
,然后生成temp3、temp4……如此递归下去,由于生成的中间变量都是存放在栈中的,所以会导致栈溢出,好在编译器一般都不允许我们这样做,直接报编译错误。
下面是一段用于理解上面内容的代码。
- 代码2:
#include <iostream>
using namespace std;
class B
{
public:
int value;
B(int n) { value = n; }
B(const B &other) { // B类型的拷贝构造函数
cout << "class B 拷贝构造函数被调用!" << endl;
value = other.value;
}
};
class A
{
public:
A(int n) { value = n; }
A(const A &other) { value = other.value; }
A(B other) { value = other.value; } // 此处会调用B的拷贝构造函数
void printA() {
cout << "value = " << value << endl;
}
private:
int value;
};
int main(void) {
A a1 = 10;
B b = 20;
A a2 = b;
a1.printA();
a2.printA();
system("pause");
return 0;
}