C++派生类与基类的转换规则

2016-02-19 11:54 10 1 收藏

下面图老师小编要向大家介绍下C++派生类与基类的转换规则,看起来复杂实则是简单的,掌握好技巧就OK,喜欢就赶紧收藏起来吧!

【 tulaoshi.com - 编程语言 】

只有公用派生类才是基类真正的子类型,它完整地继承了基类的功能。基类与派生类对象之间有赋值兼容关系,由于派生类中包含从基类继承的成员,因此可以将派生类的值赋给基类对象,在用到基类对象的时候可以用其子类对象代替。
具体表现在以下几个方面
派生类对象可以向基类对象赋值。
可以用子类(即公用派生类)对象对其基类对象赋值。如
A a1; //定义基类A对象a1
B b1; //定义类A的公用派生类B的对象b1
a1=b1; //用派生类B对象b1对基类对象a1赋值
在赋值时舍弃派生类自己的成员。
实际上,所谓赋值只是对数据成员赋值,对成员函数不存在赋值问题。请注意: 赋值后不能企图通过对象a1去访问派生类对象b1的成员,因为b1的成员与a1的成员是不同的。
假设age是派生类B中增加的公用数据成员,分析下面的用法:
a1.age=23;//错误,a1中不包含派生类中增加的成员
b1.age=21; //正确,b1中包含派生类中增加的成员
应当注意,子类型关系是单向的、不可逆的。B是A的子类型,不能说A是B的子类型。
只能用子类对象对其基类对象赋值,而不能用基类对象对其子类对象赋值,理由是显然的,因为基类对象不包含派生类的成员,无法对派生类的成员赋值。同理,同一基类的不同派生类对象之间也不能赋值。
派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化。
如已定义了基类A对象a1,可以定义a1的引用变量:
A a1; //定义基类A对象a1
B b1; //定义公用派生类B对象b1
A& r=a1; //定义基类A对象的引用变量r,并用a1对其初始化
这时,引用变量r是a1的别名,r和a1共享同一段存储单元。也可以用子类对象初始化引用变量r,将上面最后一行改为
A& r=b1;//定义基类A对象的引用变量r,并用派生类B对象b1//对其初始化
或者保留上面第3行“A& r=a1;”,而对r重新赋值:
r=b1;//用派生类B对象b1对a1的引用变量r赋值
注意: 此时r并不是b1的别名,也不与b1共享同一段存储单元。它只是b1中基类部分的别名,r与b1中基类部分共享同一段存储单元,r与b1具有相同的起始地址。
如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象。如有一函数
代码如下:

fun: void fun(A& r)//形参是类A的对象的引用变量
{
coutr.numendl;
} //输出该引用变量的数据成员num
函数的形参是类A的对象的引用变量,本来实参应该为A类的对象。由于子类对象与派生类对象赋值兼容,派生类对象能自动转换类型,在调用fun函数时可以用派生类B的对象b1作实参: fun(b1); 输出类B的对象b1的基类数据成员num的值。与前相同,在fun函数中只能输出派生类中基类成员的值。
派生类对象的地址可以赋给指向基类对象的指针变量,也就是说,指向基类对象的指针变量也可以指向派生类对象。
例11.10 定义一个基类Student(学生),再定义Student类的公用派生类Graduate(研究生), 用指向基类对象的指针输出数据。本例主要是说明用指向基类对象的指针指向派生类对象,为了减少程序长度,在每个类中只设很少成员。学生类只设num(学号),name(名字)和score(成绩)3个数据成员,Graduate类只增加一个数据成员pay(工资)。
程序如下:
[code]
#include iostream
#include string
Graduate::Graduate(int n, string nam,float s,float p):Student(n,nam,s),pay(p){ }
using namespace std;
class Student//声明Student类
{
public :
Student(int, string,float );//声明构造函数
void display( );//声明输出函数
private :
int num;
string name;
float score;
};
Student::Student(int n, string nam,float s) //定义构造函数
{
num=n;
name=nam;
score=s;
}
void Student::display( )//定义输出函数
{
coutendl″num:″numendl;
cout″name:″nameendl;
cout″score:″scoreendl;
}
class Graduate:public Student//声明公用派生类Graduate
{
public :
Graduate(int, string ,float ,float );//声明构造函数
void display( );//声明输出函数
private :
float pay;//工资
};
//定义构造函数
void Graduate::display() //定义输出函数
{
Student::display(); //调用Student类的display函数
cout″pay=″payendl;
}
int main()
{
Student stud1(1001,″Li″,87.5); //定义Student类对象stud1
Graduate grad1(2001,″Wang″,98.5,563.5); //定义Graduate类对象grad1
Student *pt=&stud1;//定义指向Student类对象的指针并指向stud1
pt-display( ); //调用stud1.display函数
pt=&grad1; //指针指向grad1
pt-display( ); //调用grad1.display函数
}

很多读者会认为: 在派生类中有两个同名的display成员函数,根据同名覆盖的规则,被调用的应当是派生类Graduate对象的display函数,在执行Graduate::display函数过程中调用Student::display函数,输出num,name,score,然后再输出pay的值。
事实上这种推论是错误的,先看看程序的输出结果:
num:1001
name:Li
score:87.5
num:2001
name:wang
score:98.5
并没有输出pay的值。
问题在于pt是指向Student类对象的指针变量,即使让它指向了grad1,但实际上pt指向的是grad1中从基类继承的部分。
通过指向基类对象的指针,只能访问派生类中的基类成员,而不能访问派生类增加的成员。所以pt-display()调用的不是派生类Graduate对象所增加的display函数,而是基类的display函数,所以只输出研究生grad1的num,name,score3个数据。
如果想通过指针输出研究生grad1的pay,可以另设一个指向派生类对象的指针变量ptr,使它指向grad1,然后用ptr-display()调用派生类对象的display函数。但这不大方便。
通过本例可以看到: 用指向基类对象的指针变量指向子类对象是合法的、安全的,不会出现编译上的错误。但在应用上却不能完全满足人们的希望,人们有时希望通过使用基类指针能够调用基类和子类对象的成员。
我们会在下一讲解决这个问题,办法是使用虚函数和多态性

来源:http://www.tulaoshi.com/n/20160219/1599068.html

延伸阅读
在前面的练习中我们一直在使用public的继续方式,即共有继续方式,对于protected和private继续方式,即保护继续与私有继续方式我们并没有讨论。 !-- frame contents -- !-- /frame contents -- 对于单个类来说,讨论保护继续与私有继续的区别意义是不大的,他们的区别只在多级继续的情况中体现。 在这里我声明一下,...
  类的多态特性是支持面向对象的语言最主要的特性,有过非面向对象语言开发经历的人,通常对这一章节的内容会觉得不习惯,因为很多人错误的认为,支持类的封装的语言就是支持面向对象的,其实不然, !-- frame contents -- !-- /frame contents -- Visual BASIC 6.0 是典型的非面向对象的开发语言,但是它的确是支持类,支...
对象(Object)是类(Class)的一个实例(Instance)。如果将对象比作房子,那么类就是房子的设计图纸。所以面向对象设计的重点是类的设计,而不是对象的设计。 对于C++程序而言,设计孤立的类是比较容易的,难的是正确设计基类及其派生类。本章仅仅论述继承(Inheritance)和组合(Composition)的概念。 注意,当前面向对象技术的应用热...
如何在VC中导出类,这是一个常有人问起的问题,下面我以一个简单的例子来说明这个问题: 首先使用Wizard创建一个Win32 Dynamic-Link Library工程,然后定义一个简单的C++类CInDLL。由于该类会被工程之外的文件所引用,所以需要对这个类进行引出。因为只有引出后所生成的DLL中才带有供足够的信息以在连接和运行时被正确引入到进程空间...
C++类对象的拷贝构造函数 作者:韩耀旭 对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=100;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。下面看一个类对象拷贝的简单例子。 #include <iostreamusing namespace std;class CA{public:CA(int b){a=b;}void Show (){c...

经验教程

911

收藏

64
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部