• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > 详解C++中基类与派生类的转换以及虚基类

详解C++中基类与派生类的转换以及虚基类

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c++虚基类,c++基类,基类和派生类,基类和派生类的关系,派生类调用基类函数等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

C++基类与派生类的转换
在公用继承、私有继承和保护继承中,只有公用继承能较好地保留基类的特征,它保留了除构造函数和析构函数以外的基类所有成员,基类的公用或保护成员的访问权限在派生类中全部都按原样保留下来了,在派生类外可以调用基类的公用成员函数访问基类的私有成员。因此,公用派生类具有基类的全部功能,所有基类能够实现的功能, 公用派生类都能实现。而非公用派生类(私有或保护派生类)不能实现基类的全部功能(例如在派生类外不能调用基类的公用成员函数访问基类的私有成员)。因此,只有公用派生类才是基类真正的子类型,它完整地继承了基类的功能。

不同类型数据之间在一定条件下可以进行类型的转换,例如整型数据可以赋给双精度型变量,在赋值之前,把整型数据先转换成为双精度型数据,但是不能把一个整型数据赋给指针变量。这种不同类型数据之间的自动转换和赋值,称为赋值兼容。现在要讨论 的问题是:基类与派生类对象之间是否也有赋值兼容的关系,可否进行类型间的转换?

回答是可以的。基类与派生类对象之间有赋值兼容关系,由于派生类中包含从基类继承的成员,因此可以将派生类的值赋给基类对象,在用到基类对象的时候可以用其子类对象代替。具体表现在以下几个方面。

1) 派生类对象可以向基类对象赋值

可以用子类(即公用派生类)对象对其基类对象赋值。如

  A a1; //定义基类A对象a1
  B b1; //定义类A的公用派生类B的对象b1
  a1=b1; //用派生类B对象b1对基类对象a1赋值
</div>


在赋值时舍弃派生类自己的成员。也就是“大材小用”,如图

实际上,所谓赋值只是对数据成员赋值,对成员函数不存在赋值问题。

请注意,赋值后不能企图通过对象a1去访问派生类对象b1的成员,因为b1的成员与a1的成员是不同的。假设age是派生类B中增加的公用数据成员,分析下面的用法:
    a1.age=23;  //错误,a1中不包含派生类中增加的成员
    b1.age=21;  //正确,b1中包含派生类中增加的成员

应当注意,子类型关系是单向的、不可逆的。B是A的子类型,不能说A是B的子类型。只能用子类对象对其基类对象赋值,而不能用基类对象对其子类对象赋值,理由是显然的,因为基类对象不包含派生类的成员,无法对派生类的成员赋值。同理,同一基类的不同派生类对象之间也不能赋值。

2) 派生类对象可以替代基类对象向基类对象的引用进行赋值或初始化

如已定义了基类A对象a1,可以定义a1的引用变量:

  A a1; //定义基类A对象a1
  B b1; //定义公用派生类B对象b1
  A& r=a1; //定义基类A对象的引用变量r,并用a1对其初始化
</div>


这时,引用变量r是a1的别名,r和a1共享同一段存储单元。也可以用子类对象初始化引用变量r,将上面最后一行改为

  A& r=b1; //定义基类A对象的引用变量r,并用派生类B对象b1对其初始化
</div>


或者保留上面第3行“A& r=a1;”,而对r重新赋值:

  r=b1; //用派生类B对象b1对a1的引用变量r赋值
</div>

注意,此时r并不是b1的别名,也不与b1共享同一段存储单元。它只是b1中基类部分的别名,r与b1中基类部分共享同一段存储单元,r与b1具有相同的起始地址。

3) 如果函数的参数是基类对象或基类对象的引用,相应的实参可以用子类对象。

如有一函数:

  fun: void fun(A& r) //形参是类A的对象的引用变量
  {
    cout<<r.num<<endl;
  } //输出该引用变量的数据成员num
</div>

函数的形参是类A的对象的引用变量,本来实参应该为A类的对象。由于子类对象与派生类对象赋值兼容,派生类对象能自动转换类型,在调用fun函数时可以用派生类B的对象b1作实参:

   fun(b1);
</div>


输出类B的对象b1的基类数据成员num的值。

与前相同,在fun函数中只能输出派生类中基类成员的值。

4) 派生类对象的地址可以赋给指向基类对象的指针变量,也就是说,指向基类对象的指针变量也可以指向派生类对象。

[例] 定义一个基类Student(学生),再定义Student类的公用派生类Graduate(研究生), 用指向基类对象的指针输出数据。本例主要是说明用指向基类对象的指针指向派生类对象,为了减少程序长度,在每个类中只设很少成员。学生类只设num(学号),name(名字)和score(成绩)3个数据成员,Graduate类只增加一个数据成员pay(工资)。程序如下:

#include <iostream>
#include <string>
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( ) //定义输出函数
{
  cout<<endl<<"num:"<<num<<endl;
  cout<<"name:"<<name<<endl;
  cout<<"score:"<<score<<endl;
}
class Graduate:public Student //声明公用派生类Graduate
{
public:
 Graduate(int, string ,float,float); //声明构造函数
 void display( ); //声明输出函数
private:
 float pay; //工资
};
//定义构造函数
Graduate::Graduate(int n, string nam,float s,float p):Student(n,nam,s),pay(p){ }
void Graduate::display() //定义输出函数
{
  Student::display(); //调用Student类的display函数
  cout<<"pay="<<pay<<endl;
}
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函数
}
</div>

下面对程序的分析很重要,请大家仔细阅读和思考。

很多读者会认为,在派生类中有两个同名的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
</div>

前3行是学生stud1的数据,后3行是研究生grad1的数据,并没有输出pay的值。

问题在于pt是指向Student类对象的指针变量,即使让它指向了grad1,但实际上pt指向的是grad1中从基类继承的部分。

通过指向基类对象的指针,只能访问派生类中的基类成员,而不能访问派生类增加的成员。所以pt->display()调用的不是派生类Graduate对象所增加的display函数,而是基类的display函数,所以只输出研究生grad1的num,name,score3个数据。

如果想通过指针输出研究生grad1的pay,可以另设一个指向派生类对象的指针变量ptr,使它指向grad1,然后用ptr->display()调用派生类对象的display函数。但这不大方便。

通过本例可以看到,用指向基类对象的指针变量指向子类对象是合法的、安全的,不会出现编译上的错误。但在应用上却不能完全满足人们的希望,人们有时希望通过使用基类指针能够调用基类和子类对象的成员。如果能做到这点,程序人员会感到方便。后续章节将会解决这个问题。办法是使用虚函数和多态性。

C++虚基类详解
多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如非常经典的菱形继承层次。如下图所示:

类A派生出类B和类C,类D继承自类B和类C,这个时候类A中的成员变量和成员函数继承到类D中变成了两份,一份来自 A-->B-->D 这一路,另一份来自 A-->C-->D 这一条路。

在一个派生类中保留间接基类的多份同名成员,虽然可以在不同的成员变量中分别存放不同的数据,但大多数情况下这是多余的:因为保留多份成员变量不仅占用较多的存储空间,还容易产生命名冲突,而且很少有这样的需求。

为了解决这个问题,C++提供了虚基类,使得在派生类中只保留间接基类的一份成员。

声明虚基类只需要在继承方式前面加上 virtual 关键字,请看下面的例子:

#include <iostream>
using namespace std;
class A{
protected:
  int 



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C++ 基类指针和子类指针相互赋值的实现方法
  • 浅谈C++ 基类指针和子类指针的相互赋值
  • 详谈C++中虚基类在派生类中的内存布局
  • 实例讲解C++编程中的虚函数与虚基类
  • 详解C++中基类与派生类的转换以及虚基类
  • C++中基类和派生类之间的转换实例教程

相关文章

  • 2017-05-28浅谈c++的编译和运行
  • 2017-05-28C语言二进制思想以及数据的存储
  • 2017-05-28解决C++中重定义的方法总结
  • 2017-05-28C语言实现BMP转换JPG的方法
  • 2017-05-28C语言以数据块的形式读写文件实例代码
  • 2017-05-28重构-C++实现矩阵的简单实例
  • 2017-05-28socket多人聊天程序C语言版(二)
  • 2017-05-28VC中使用ADO开发数据库应用程序简明教程
  • 2017-05-28C++中用substr()函数消除前后空格的解决方法详解
  • 2017-05-28浅析C语言中的数组及字符数组

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C++ read函数读入int整形数据
    • C++在成员函数中使用STL的find_if函数实例
    • 关于define与C 的内存
    • c语言实现系统时间校正工具代码分享
    • C语言栈顺序结构实现代码
    • 四叉树有损位图压缩处理程序示例
    • C++ 赋值构造函数注意点介绍
    • C语言中多维数组的内存分配和释放(malloc与free)的方法
    • 一些C语言中字符串的算法问题解决实例小结
    • C++编程异常处理中try和throw以及catch语句的用法

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有