拷贝构造函数
拷贝构造实际上也是构造函数的一种。当你创建了拷贝构造函数的之后,系统就不再提供默认的构造函数了。
作用:
用一个已经存在的对象去初始化另一个对象。
赋值并不会调用拷贝构造函数。
class test
{
public:
test()
{
cout << "构造函数的调用" << endl;
}
test(test& p)
{
cout << "拷贝的调用" << endl;
}
};
int main()
{
test tmp2;
test tmp1;
tmp1 = tmp2;
return 0;
}
//输出结果
//构造函数的调用
//构造函数的调用
格式:
类名(类名& p)
{
函数体;
}
特点:
- 拷贝构造函数与类名同名
- 拷贝构造函数没有返回值
- 拷贝构造函数只有一个参数,是同类对象的引用,可加const修饰。
- 编译器会自动创建默认构造,并自动调用。程序员可以手动添加,但是添加之后编译器就不再添加默认的拷贝构造以及普通构造函数了。编译器提供的默认构造是具有值拷贝作用的。
关于拷贝构造函数参数的问题
拷贝构造的参数为什么是引用?
上面说了,拷贝构造函数的参数是一个引用,但是这是为什么呢?不适用引用不可以吗?当然,比可以。
class test
{
public:
test()
{
}
test(test p) //此处报错
{ //错误详情:“test”: 非法的复制构造函数: 第一个参数不应是“test”
cout << "拷贝的调用" << endl;
}
};
那么,为什么呢?
很好理解,问题出在函数参数的传递方式的特点上。
函数有值传参的方式,上程序就是。值传递的话,调用时会创建一个新的对象p。
那么这个新对象是怎么来的呢?
首先看看我们是怎么调用的。
test tmp2;
test tmp1(tmp2);
想想以前的函数值传参。
void fun(int c)
{
cout << c << endl;
}
int a = 10;
fun(c);
//输出结果为10
那么为什么c是10呢?当然是由a赋值给c的啊。
同理p的值是由tmp2赋值而来的。嗯?由一个对象赋值给另一个对象?
这需要调用拷贝函数啊!
然后又是值传递,又需要调用拷贝构造函数。
子子孙孙无穷尽也。。。。
而引用就不存在这个问题,因为引用只是一个个别名,具体操作的还是tmp2
构造函数的调用时机:
一个已经存在的对象赋值初始化另一个对象
class test
{
private:
int a;
int b;
public:
test(int m_a, int m_b) : a(m_a), b(m_b)
{
cout << "构造函数的调用" << endl;
}
test(test& p)
{
cout << "拷贝构造函数的调用" << endl;
a = p.a;
b = p.b;
}
void show(void)
{
cout << "a = " << a << " ; b = " << b << endl;
}
};
int main()
{
test tmp1(1, 2);
tmp1.show();
test tmp2(tmp1);
tmp2.show();
return 0;
}
//输出结果
//构造函数的调用
//a = 1 ; b = 2
//拷贝构造函数的调用
//a = 1 ; b = 2
一个对象充当函数的参数
这点在拷贝构造的参数为什么是引用的时候说过了。
函数返回值类型是对象
class test
{
public:
test()
{
cout << "构造函数的调用" << endl;
}
test(test& p)
{
cout << "拷贝的调用" << endl;
}
};
test fun()
{
test t;
return t;
}
int main()
{
fun();
return 0;
}
//输出结果:
//构造函数的调用
//拷贝的调用
分析输出结果,可知,输出“构造函数的调用”是因为==test t;==语句,但是为什么会输出拷贝调用呢?
是因为函数返回一个局部变量或者局部对象时,会在内核空间创建一个新的变量或者对象,然后将函数出栈,销毁局部变量和对象。尽管没有接收,但是这是一个必要的步骤。若有对象接收直接将内核空间的对象的值赋值给接收的对象。
特殊情况:
class test
{
public:
test()
{
cout << "构造函数的调用" << endl;
}
test(test& p)
{
cout << "拷贝的调用" << endl;
}
};
test fun()
{
test t;
return t;
}
int main()
{
test tmp = fun();
return 0;
}
//输出结果
//构造函数的调用
//拷贝的调用
由之前的结论可以知道,fun函数返回的时候会在内核空间创建一个匿名对象,会调用一次拷贝构造,然后再使用这个匿名对象去初始化tmp,然后再调用一次拷贝构造。但事实上却只调用了一次拷贝构造,这是为什么呢?
这是因为编译器对这个过程做了优化,毕竟如果按照上面的过程中,内核空间的对象只起到了一个中转作用,劳民伤财,域是编译器就直接把匿名对象包装成tmp了,毕竟他俩一模一样。
深拷贝和浅拷贝:
见:添加链接描述