当前课程知识点:C++语言程序设计进阶 > 第七章 继承与派生 > 派生类成员的标识与访问 > 访问从基类继承的成员
当派生类与基类中有相同成员时:
若未特别限定,则通过派生类对象使用的是派生类中的同名成员。
如要通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符(::)来限定。
#include <iostream> using namespace std; class Base1 { public: int var; void fun() { cout << "Member of Base1" << endl; } }; class Base2 { public: int var; void fun() { cout << "Member of Base2" << endl; } }; class Derived: public Base1, public Base2 { public: int var; void fun() { cout << "Member of Derived" << endl; } }; int main() { Derived d; Derived *p = &d; //访问Derived类成员 d.var = 1; d.fun(); //访问Base1基类成员 d.Base1::var = 2; d.Base1::fun(); //访问Base2基类成员 p->Base2::var = 3; p->Base2::fun(); return 0; }
如果从不同基类继承了同名成员,但是在派生类中没有定义同名成员,“派生类对象名或引用名.成员名”、“派生类指针->成员名”访问成员存在二义性问题
解决方式:用类名限定
class A { public: void f(); }; class B { public: void f(); void g() }; class C: public A, piblic B { public: void g(); void h(); };
如果定义:C c1; 则 c1.f() 具有二义性 而 c1.g() 无二义性(同名隐藏) 例7-7 多继承时的二义性和冗余问题
//7_7.cpp #include <iostream> using namespace std; class Base0 { //定义基类Base0 public: int var0; void fun0() { cout << "Member of Base0" << endl; } }; class Base1: public Base0 { //定义派生类Base1 public: //新增外部接口 int var1; }; class Base2: public Base0 { //定义派生类Base2 public: //新增外部接口 int var2; }; 例7-7 多继承时的二义性和冗余问题 class Derived: public Base1, public Base2 { public: int var; void fun() { cout << "Member of Derived" << endl; } }; int main() { //程序主函数 Derived d; d.Base1::var0 = 2; d.Base1::fun0(); d.Base2::var0 = 3; d.Base2::fun0(); return 0; }
大家好
欢迎回来继续学习
C++语言程序设计
这一节呢
我们来考虑这样一个问题
基类的成员
继承到派生类中以后
访问的时候
有没有什么
特殊需要注意的问题呢
会不会出现一些二异性
或者无法访问的情况呢
我们知道
如果说
你写着派生类里面的成员
有于基类中成员相同的时候
就会有一些麻烦了
当我们写派生类的时候
如果在派生类中定义了
与基类中的成员同名的成员
会怎么样呢
在这种情况下
如果不加特别地限定
那么通过派生类对象
使用的就是派生类中
自己新定义的同名成员
那么如果
一定要通过派生类的对象
去访问那些从基类继承过来的
又被隐藏的同名成员
该怎么办呢
也是有办法的
这个时候呢
就要使用基类名
和作用域限定符来限定
在下面的例子中呢
我们会看到
派生类中的同名成员呢
就隐藏了从基类继承过来的成员
在这里呢
我们在基类Base1类中定义了
变量var和函数fun
在另一个基类Base2里面
也定义了同名的变量和函数
接下来呢
Derived类继承了Base1
继承了Base2
同时它自己也定义了
同名的变量和函数
现在我们来看
在主函数中
我们定义一个Derived类的对象
通过对象名去调用函数的时候
比如调用函数fun
它到底访问的是谁
现在我们通过单步跟踪
观察一下
好
现在即将要调用这个fun函数
我们按f11
看到进到这个Derived对象
自己所属类的fun函数中了
为什么呢
因为在Derived类里面
定义了同名的函数
那它就屏蔽从基类
或者说隐藏了从基类
继承过来的函数了
那么这样
如果依然要访问
从基类继承过来的数据成员
和函数成员
怎么办呢
那么这里的办法就是
通过基类名限定来访问
那我们再跟踪到
这个fun函数里面去
看
进到基类1的fun函数里面了
同样呢
如果你用指针去访问
也是一样的
比如说我们将这个Derived指针
Derived地址
这个指针p
用来访问对象
那么访问的肯定是
Derived对象了
但是我们刻意要调用
它从Base2继承过来的这个函数
怎么调用
通过类名限定
就进到Base2的fun函数里面了
当我们的派生类
从不同的基类继承的时候呢
如果这些不同的基类中
有同名的成员
那么这些成员
继承到派生类中以后
如何区分它们呢
如何访问它们呢
那这个时候呢
当然访问肯定就存在了二异性
你不能直接用对象名
或者引用名.成员名
或者是说派生类指针指向成员名
这样的方式访问了
那要解决这个二异性呢
当然有简单的办法
就是用类名来限定
那么我们看这个例子中呢
我们在类a中定义了函数f
在类b中也定义了函数f
那么在类b中定义了函数g
在派生类c里面也定义了函数g
C以公有方式继承a 继承b
C类自己还新定义了
一个自己的函数h
这个时候
如果我们定义了C1类的对象c1
我们用c1.f的方式来调用函数f
就具有二异性了
因为它有两个函数f
是一个是从a类继承来的
一个是从b类继承来的
如果调用c1.g就没有二异性
因为根据同名隐藏规则
在C类中定义了函数g
那么直接调用的
就是C类自己的函数g
那么我们一定要调用函数f
怎么办呢
可以用类名来限定
这是一种办法
另一种办法就是说
我们在C类中
可以自己再定义一个函数f
在这个C类的函数f里面
根据需要去选择调用A f
或者是B f
这两种办法都是可以的
那这是这个关于多继承的时候
有可能发生二异性
和冗余问题的例题
在这我们定义一个Base0类
它里面定义了变量var0 fun0
然后Base1类继承了Base0
Base2类也继承了Base0
接下来
我们让Derived类继承Base1
和Base2
现在 Derived类里面
到底有些什么成员呢
我们来看这个示意图
实际上这个继承关系
相当于是这样的
Base1继承Base0
Base2也继承Base0
然后Derived继承Base1 Base2
那我们看这个示意图
这个示意图
实际上在Derivd类的对象里面
它就包含着
从Base0继承来的成员
包含了两次
为什么呢
因为Derived类
它有两个直接基类
所以它会包含
从Base1继承来的成员
和从Base2继承来的成员
但是Base1的成员里面
就包含了从Base0继承的成员
Base2的成员
也包含了
从Base0继承的成员
所以呢
Base0里面定义的数据成员
就在Derived类里面
出现了两份
那么当然这个时候
如果你按这样d.var0访问
就就二异性了
所以我们就要用类名来限定
我们是通过对象d
访问从Base1
继承来的var0 fun0
还是访问
从Base2继承来的var0 fun0
这样来做呢
虽然这个二异性的问题解决了
没有二异性了
但是我们看
对象的存储
它的对象成员的存储
它是冗余的
冗余第一个问题
是会带来空间的浪费
第二个问题
是会带来不一致性
使用的时候会发生一些混淆
如果这两个本改是一回事的成员
被分别置与了不同的值
那这个结果又如何解释呢
结果就没什么意义了
所以这种情况呢
通过这个例题看到
有可能发生二异性
和冗余的情况
其实是我们写程序
应该避免的
-导学
--导学
-继承的基本概念和语法
-第七章 继承与派生--继承的基本概念和语法习题
-继承方式
-第七章 继承与派生--继承方式
-基类与派生类类型转换
-第七章 继承与派生--基类与派生类类型转换
-派生类的构造和析构
--派生类的构造函数
--派生类的析构函数
--第七章 继承与派生--派生类的构造和析构
-派生类成员的标识与访问
--虚基类
-第七章 继承与派生--派生类成员的标识与访问
-小结
--小结
-综合实例
--第七章综合实例
-实验七
--实验七
-导学
--导学
-第八章 多态性--导学
-运算符重载
--运算符重载的规则
-第八章 多态性--运算符重载
-虚函数
--虚函数
--虚析构函数
--虚表与动态绑定
-第八章 多态性--虚函数
-抽象类
--抽象类
--第八章 多态性--抽象类
-override与final
-第八章 多态性--override与final
-小结
--第八章小结
-综合实例
--第八章综合实例
-实验八
--实验八
- 第八章讲义
-导学
--导学
-模板
--函数模板
--类模板
-第九章 模板与群体数据--模板
-线性群体
--线性群体的概念
-第九章 模板与群体数据--线性群体
-数组
--数组类模板
-链表
--链表类模板
-第九章 模板与群体数据--链表
-栈
--栈类模板
--栈类模板课后习题
--例9-9 栈的应用课后习题
-队列
--队列类模板
-第九章 模板与群体数据--队列
-排序
--排序概述
--插入排序
--选择排序
--交换排序
-第九章 模板与群体数据--排序
-查找
--查找
--查找课后习题
-小结
--小结
-综合实例
--综合实例
-实验九
--实验九
- 第九章讲义
-导学
--导学
-泛型程序设计及STL的结构
--STL简介
-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构
-迭代器
--迭代器
-第十章 泛型程序设计与C++标准模板库--迭代器
-容器的基本功能与分类
-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类
-顺序容器
--顺序容器的特征
--第十章 泛型程序设计与C++标准模板库--顺序容器
-关联容器
--集合
--映射
-第十章 泛型程序设计与C++标准模板库--关联容器
-函数对象
--函数对象
--函数适配器
-算法
--算法
-小结
--第十章小结
-综合实例
--综合实例
-实验十
--实验十
- 第十章讲义
-导学
--导学
-I/O流的概念及流类库结构
-第十一章 流类库与输入/输出--I/O流的概念及流类库结构
-输出流
--输出流概述
--向文本文件输出
--向二进制文件输出
--向字符串输出
-第十一章 流类库与输入/输出--输出流
-输入流
--输入流概述
--输入流应用举例
--从字符串输入
-第十一章 流类库与输入/输出--输入流
-输入/输出流
--输入/输出流
-第十一章 流类库与输入/输出--输入/输出流
-小结
--小结
-综合实例
--综合实例
-实验十一
--实验十一
- 第十一章讲义
-导学
--第12章导学
-异常处理的思想与程序实现
-第十二章 异常处理--异常处理的思想与程序实现
-异常处理中的构造与析构
-第十二章 异常处理--异常处理中的构造与析构
-标准程序库异常处理
-第十二章 异常处理--标准程序库异常处理
-小结
--第12章小结
-综合实例
--综合实例
-实验十二
--实验十二
- 第十二章讲义