1,为什么拷贝构造函数传入的必须是引用?
如果拷贝构造函数中的参数不是一个引用,即形如CClass(const CClass c_class),那么就相当于采用了传值的方式(pass-by-value),而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函数。因此拷贝构造函数的参数必须是一个引用。
需要澄清的是,传指针其实也是传值,如果上面的拷贝构造函数写成CClass(const CClass* c_class),也是不行的。事实上,只有传引用不是传值外,其他所有的传递方式都是传值。#include<iostream>
using namespace std;
class CExample
{
private:
int m_nTest;
public:
CExample(int x) : m_nTest(x) //带参数构造函数
{
cout << "constructor with argument"<<endl;
}
// 拷贝构造函数,参数中的const不是严格必须的,但引用符号是必须的
CExample(const CExample & ex) //拷贝构造函数
{
m_nTest = ex.m_nTest;
cout << "copy constructor"<<endl;
}
CExample& operator = (const CExample &ex) //赋值函数(赋值运算符重载)
{
cout << "assignment operator"<<endl;
m_nTest = ex.m_nTest;
return *this;
}
void myTestFunc(CExample ex)
{
}
};
int main(void)
{
CExample aaa(2); // 实例化对象,调用构造函数
CExample bbb(3); // 实例化对象,调用构造函数
bbb = aaa; // 赋值函数
CExample ccc = aaa; // 拷贝构造函数,对象ccc不存在,通过对象aaa实例化,
bbb.myTestFunc(aaa); // 拷贝构造函数 作为实参传入到一个函数
return 0;
}
结果
constructor with argument // CExample aaa(2);
constructor with argument // CExample bbb(3);
assignment operator // bbb = aaa;
copy constructor // CExample ccc = aaa;
copy constructor // bbb.myTestFunc(aaa);
横向拓展:
什么是拷贝构造函数?
构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存。(构造函数的命名必须和类名完全相同)
什么时候调用拷贝构造函数?
在C++中,3种对象需要复制,此时拷贝构造函数会被调用
1)一个对象以值传递的方式传入函数体
2)一个对象以值传递的方式从函数返回
3)一个对象需要通过另一个对象进行初始化
下面说说深拷贝与浅拷贝:
浅拷贝:如果复制的对象中引用了一个外部内容(例如分配在堆上的数据),那么在复制这个对象的时候,让新旧两个对象指向同一个外部内容,就是浅拷贝。(指针虽然复制了,但所指向的空间内容并没有复制,而是由两个对象共用,两个对象不独立,删除空间存在)
深拷贝:如果在复制这个对象的时候为新对象制作了外部对象的独立复制,就是深拷贝。
赋值函数
当一个类的对象向该类的另一个对象赋值时,就会用到该类的赋值函数。
当没有重载赋值函数(赋值运算符)时,通过默认赋值函数来进行赋值操作
A a;
A b;
b=a;
强调:这里a,b对象是已经存在的,是用a 对象来赋值给b的!!
拷贝构造函数与赋值函数的区别
1)拷贝构造函数是一个对象初始化一块内存区域,这块内存就是新对象的内存区,而赋值函数是对于一个已经被初始化的对象来进行赋值操作。
2)一般来说在数据成员包含指针对象的时候,需要考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。拷贝构造函数大多数情况下是复制,而赋值函数是引用对象
3)实现不一样。拷贝构造函数首先是一个构造函数,它调用时候是通过参数的对象初始化产生一个对象。赋值函数则是把一个新的对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检察一下两个对象是不是同一个对象,如果是,不做任何操作,直接返回。