C++学习笔记49——继承中的复制构造函数与赋值操作符

1,什么时候需要定义自己的复制构造函数?

只包含类类型或内置类型数据成员、不含指针的类一般可以使用合成操作,复制、赋值或撤销这样的成员不需要特殊控制。
换言之,如果数据中有指针,为了避免浅复制,就需要定义自己的复制构造函数、赋值操作符和析构函数了。

一旦定义了自己的复制构造函数或赋值操作符,则默认定义就会被覆盖掉。

2,一个复制构造函数的例子

/*******************************************************************/
//  派生类的构造函数
/*******************************************************************/
class Base4
{
public:
	Base4(int ii, int jj) :i(ii), j(jj) { cout << "调用基类的一般构造函数" << endl; }//基类的一般构造函数
	Base4() :i(0), j(5203132) { cout << "调用基类的默认构造函数" << endl; }//基类的默认构造函数
	Base4(const Base4& b4);//复制构造函数
	void show_base() { cout << i << "\t" << j<<"\t"; }
public:
	int i;
private:
	int j;
};

class D4 :public Base4
{
public:
	D4():k(0) {}//派生类的默认构造函数
	D4(int ii, int jj, int kk) :Base4(ii, jj), k(kk) { }//派生类的一般构造函数	
	D4(int kk) : k(kk) {}//派生类的一般构造函数,隐式调用基类的默认构造函数
	D4(const D4& d4);//派生类的复制构造函数

	int k;
	void show_d() { show_base(); cout << k << endl; }
};
//复制构造函数的定义
Base4::Base4(const Base4& b4)
{
	cout << "调用基类的复制构造函数" << endl;
	i = b4.i;
	j = b4.j;
}

D4::D4(const D4& d4):Base4(d4) //在初始化列表中使用,与前一节的派生类构造函数定义方法相同
{
	cout << "调用派生类的复制构造函数" << endl;
	k = d4.k;
}

int main()
{
	cout << "先生成一个成员d1(520, 3132, 1314):" << endl;
	D4 d1(520, 3132, 1314);
	d1.show_d();

	cout << "\n开始复制:" << endl;
	D4 d2(d1);
	d2.show_d();
}
执行效果如下:
C++学习笔记49——继承中的复制构造函数与赋值操作符_第1张图片

注意,在定义派生类的复制构造函数时,用到了基类的复制构造函数,并且是在初始化列表中使用的。这一点与上一节,定义派生类的普通构造函数相同。

3,设想1:把基类的复制构造函数从初始化列表中移到函数定义体中会如何?

其他代码不变,将派生类的复制构造函数的定义改为如下所示:
D4::D4(const D4& d4)
{
	Base4(d4);
	cout << "调用派生类的复制构造函数" << endl;
	k = d4.k;
}
编译直接报错:error C2082: 形参“d4”的重定义
显然,编译器把“Base4(d4);”当成了定义变量d4了。

4,设想2:不显式调用基类的复制构造函数会怎样?

其他代码不变,将派生类的复制构造函数的定义改为如下所示:
D4::D4(const D4& d4)
{
	cout << "调用派生类的复制构造函数" << endl;
	k = d4.k;
}
执行结果如下:
C++学习笔记49——继承中的复制构造函数与赋值操作符_第2张图片

很明显,当我们不显式调用基类的复制构造函数时,编译器隐式调用了基类的默认构造函数。造成的结果是:基类部分是默认值,派生类部分是另一个对象的值。这种奇怪的现象当然通常不是我们想要的。

5,赋值操作符

赋值操作符与复制构造函数实现类似,不再赘述。
提供一个完整的例子:
class Base4
{
public:
	Base4(int ii, int jj) :i(ii), j(jj) { cout << "调用基类的一般构造函数" << endl; }//基类的一般构造函数
	Base4() :i(0), j(5203132) { cout << "调用基类的默认构造函数" << endl; }//基类的默认构造函数
	Base4(const Base4& b4);//复制构造函数
	Base4& operator=(const Base4& b4);
	void show_base() { cout << i << "\t" << j<<"\t"; }
public:
	int i;
private:
	int j;
};

class D4 :public Base4
{
public:
	D4() :k(0) { cout << "调用派生类的默认构造函数" << endl; }//派生类的默认构造函数
	D4(int ii, int jj, int kk) :Base4(ii, jj), k(kk) { }//派生类的一般构造函数
	D4(int kk) : k(kk) {}//派生类的一般构造函数,隐式调用基类的默认构造函数
	D4(const D4& d4);//派生类的复制构造函数
	D4& operator=(const D4&);

	int k;
	void show_d() { show_base(); cout << k << endl; }
};

//定义基类的复制构造函数
Base4::Base4(const Base4& b4)
{
	cout << "调用基类的复制构造函数" << endl;
	i = b4.i;
	j = b4.j;
}

//定义基类的赋值操作符
Base4& Base4::operator=(const Base4& b4)
{
	cout << "调用基类的=" << endl;
	if (this != &b4)
	{
		i = b4.i;
		j = b4.j;
	}
	return *this;	
}

//定义基类的复制构造函数
D4::D4(const D4& d4)
{
	cout << "调用派生类的复制构造函数" << endl;
	k = d4.k;
}

//定义派生类的赋值操作符
D4& D4::operator=(const D4& d4)
{
	cout << "调用派生类的=" << endl;
	if (this != &d4)
	{
		Base4::operator=(d4);//注意这种调用方式
		k = d4.k;
	}
	return *this;
}

//主函数
int main()
{
	cout << "先生成一个成员d1(520, 3132, 1314):" << endl;
	D4 d1(520, 3132, 1314);
	d1.show_d();

	cout << "\n开始复制:" << endl;
	D4 d2(d1);
	d2.show_d();

	cout << "\n开始赋值:" << endl;
	D4 d3;
	d3 = d1;
	d3.show_d();
	
	return 0;
	
}

执行结果:
C++学习笔记49——继承中的复制构造函数与赋值操作符_第3张图片

请注意基类的赋值操作符的调用方式。与基类的复制构造函数不同,基类的赋值操作符应当出现在派生类的赋值操作符的定义体中。
另有一点需要注意,如果我们写: D4 d3 = d1;
实际调用的是复制构造函数,而不是赋值操作符。

你可能感兴趣的:(学习笔记)