C++继承中的赋值兼容规则你了解多少???

C++ 赋值兼容规则

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。

1.派生类对象 可以赋值给 基类的对象

   A a1; //定义基类A对象a1
   B b1; //定义类A的公用派生类B的对象b1
   a1=b1; //用派生类B对象b1对基类对象a1赋值
  • 在赋值时舍弃派生类自己的成员,只进行数据成员的赋值。
  • 实际上,所谓赋值只是对数据成员赋值,对成员函数不存在赋值问题,内存中数据成员和成员函数是分开的。
  • 请注意: 赋值后不能企图通过对象a1去访问派生类对象b1的成员,因为b1的成员与a1的成员是不同的
  • 只能用子类对象对其基类对象赋值,而不能用基类对象对其子类对象赋值,理由是显然的,两种对象的大小是不同的,因为基类对象不包含派生类的成员,无法对派生类的成员赋值。同理,同一基类的不同派生类对象之间也不能赋值。

2.派生类对象 可以赋值给 基类的引用

	A a1; //定义基类A对象a1
    B b1; //定义公用派生类B对象b1
    A& r1=a1; //定义基类A对象的引用变量r1(A的别名是r),并用a1对其初始化这时,r和a1共享同一段存储单元。也可以用派生类对象初始化引用变量r,将上面最后一行改为
    A& r2=b1;//定义基类A对象的引用变量r2,并用派生类B对象b1//对其初始化
  • 引用的底层实现就是指针,因此引用同理指针
  • 此时r2并不是b1的别名,也不与b1共享同一段存储单元。它只是b1中基类部分的别名(个人认为这里的r定义为A类的引用,所以它的有效范围就只有A类那么大)

如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象。如有一函数fun():

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
class A
{
    
    
public:
	int num = 10;

};
class B: public A
{
    
    
public:
	int num = 20;
};
void fun(A &a1)
{
    
    
	cout << a1.num << endl;
}
int main()
{
    
    
	A a1;
	B b1;
	A &r1 = a1;
	cout << r1.num << endl;
	A &r2 = b1;
	cout << r2.num << endl;
	cout << a1.num << endl;
	cout << b1.num << endl;
	fun(a1);
	fun(b1);
	system("pause");
	return 0;
	
}

函数的形参是类A的对象的引用变量,本来实参应该为A类的对象。由于子类对象与派生类对象赋值兼容,派生类对象能自动转换类型, 在调用fun函数时可以用派生类B的对象b1作实参: fun(b1); 输出类B的对象b1的基类数据成员num的值。与前相同,在fun函数中只能输出派生类中基类成员的值。
输出结果:
在这里插入图片描述

3.派生类对象 可以赋值给 基类的指针

	A a1;
	B b1;
	A* a2 = &a1;//基类指针a2指向基类对象a1;
	A* a3 = &b1;//基类指针指向派生类对象b1;

看看接下来代码运行结果:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
class A
{
    
    
public:
	int num = 10;
	void print()
	{
    
    
		cout << "this is A" << endl;
	}

};
class B: public A
{
    
    
public:
	int num = 20;
	void print()
	{
    
    
		cout << "this is B" << endl;
	}
};
int main()
{
    
    
	A a1;
	B b1;
	A* a2 = &a1;
	A* a3 = &b1;
	a2->print();
	a3->print();
	cout << a2->num << endl;
	cout << a3->num << endl;
	system("pause");
	return 0;
	
}

很多人以为a3指向的是派生类的对象b1,而派生类中根据同名隐藏规则,被调用的应该是派生类的print()函数,分别输出"this is A" 和 “this is B” 但结果并非如此。
在这里插入图片描述
问题在于:a3是指向派生类对象b1的指针变量,但实际上a3指向的是b1中从基类继承的部分(它指向的空间只能是基类中数据成员那么大的空间)。通过指向基类对象的指针,只能访问派生类中的基类成员,而不能访问派生
类增加的成员。所以a3->print()调用的不是派生类b1对象所增加的print()函数,而是基类的print()函数,所以只输出"this is A".

猜你喜欢

转载自blog.csdn.net/cckluv/article/details/111979176