C++ 派生类函数重载与虚函数继承详解
类的关系图:
一、作用域与名字查找
1.作用域的嵌套
派生类的作用域嵌套在基类之内
Bulk_quote bulk;
cout<< bulk.isbn();
名字isbn解析过程:
- 因为我们是通过Bulk_quote的对象调用isbn的,所以首先在Bulk_quote中查找,这一步没有找到名字isbn。
- 因为 Bulk_quote是Disc_quote 的派生类,所以接下来在Disc_quote中查找,仍然找不到。
- 因为Disc_quote是Quote的派生类,所以接着查找Quote;此时找到了名字isbn,所以我们使用的isbn最终被解析为Quote中的isbn。
2.在编译时进行名字查找
成员名字的查找类型由静态类型决定
//给Disc_quote添加一个成员,返回折扣政策
class Disc_quote : public Quote {
public :
std::pair<int ,double) discount_policy() const
{return {quantity,discount};}
};
我们只能通过Disc_quote及其派生类对象来使用discount_policy。
Bulk_quote bulk;
Bulk_qoute *bulkP = &bulk; //静态类型与动态类型一致
Quote *itemP = &bulk; //静态类型为Quote,动态类型不一定
bulkP->discount_policy(); //正确:bulkP的类型是Bulk_quote*
itemP->discount_policy(); //错误:itemP的类型是Quote*
尽管在bulk中确实含有一个名为discount_policy的成员,但是该成员对于itemP却是不可见的。
itemP的类型是Quote的指针,意味着对discount_policy的搜索将从Quote开始。
显然Quote不包含名为discount_policy的成员,所以我们无法通过Quote的对象、引用或指针调用discount_policy。
3.名字冲突与继承
派生类可以重用基类中的名字,由于派生类的作用域嵌套在基类中,所以会隐藏基类的同名变量
派生类成员隐藏同名的基类成员
struct Base{
Base():mem(0){}
protected:
int mem;
};
struct Derived : Base{//struct默认public继承
Derived(int i) : mem(i){};
int get_mem() {return mem;}
protected:
int mem;
};
get_mem
返回的是在Derived中的mem
Derived d(42);
cout<<d.get_mem()<<endl; //打印42
4.通过作用域运算符来使用隐藏的成员
struct Derived : public Base{
int get_base_mem() {return Base::mem;}
//...
};
d.get_base_mem(); //输出0
二、同名函数隐藏与虚函数覆盖
1.几种必须区分的情况
派生类函数形式 | 与基类同名函数的关系 | 形参列表 | 绑定方式 |
---|---|---|---|
非虚函数 | 隐藏基类同名函数 | 可相同可不同 | 静态绑定 |
虚函数 | 覆盖基类虚函数 | 必须相同 | 动态绑定 |
使用基类的引用或指针调用虚函数时,会发生动态绑定
- 当派生类有基类的同名虚函数且该函数不是虚函数时,无论两个同名函数的参数是否相同。
- 由于派生类的作用域嵌套在基类内部,所以都会隐藏同名的基类函数
- 由于不是虚函数,所以即使两函数参数相同,也不会发生动态绑定
//情况1举例
class A{
public :
//基类的print不是虚函数
void print() const
{cout<<"class A"<<endl;};
};
class B : public A{
public:
//B隐藏了A的同名函数
void print() const
{cout<<"class B"<<endl;}
};
void Test_Print(const A &a){//传入基类的指针
//由于基类的print不是虚函数,所以不会动态绑定
//.print()的结果取决于a的静态类型
//所以无论传入的是A还是B,都打印class A
a.print();
}
int main(){
A a;
B b;
Test_Print(a); //打印class A
Test_Print(b); //打印class A;因为传入参数的静态类型是A
return 0;
}
当派生类有基类的同名函数且该函数是虚函数时
参数列表相同,实现覆盖基类虚函数,可以发生动态绑定
//情况2:参数列表相同时,虚函数被覆盖
class A{
public :
//基类的print是虚函数
void print() const
{cout<<"class A"<<endl;};
}; /*
class B和Test_Print都不变
*/ int main(){
A a;
B b;
Test_Print(a); //打印class A
Test_Print(b); //打印class B;因为发生了动态绑定
return 0;
}
参数列表不相同,相当于派生类定义了一个新函数,隐藏了基类虚函数,基类的虚函数没有被覆盖
class A{
public :
//基类的print是虚函数
void print() const
{cout<<"class A"<<endl;};
}; class B : public A{
public:
//B的print(int i)与基类虚函数同名
//但参数列表不同,定义了一个新函数
//基类的虚函数print()没有被覆盖
void print(int i) const
{cout<<"print(int i)"<<endl;}
}; void Test_Print(const A &a){//传入基类的指针
a.print();
} int main(){
A a;
B b;
//打印class A
Test_Print(a);
//打印class A;
//因为派生类没有重载虚函数,继续调用基类虚函数
Test_Print(b);
//打印print(int i)
//调用派生类新增的函数print(int i)
b.print(42);
return 0;
}
2.一个更复杂的例子
例子出自《C++ Primer》P550
//类的定义
class Base{
public :
virtual int fcn();
};
class D1 : public Base{
public:
//隐藏基类的fcn,这个fcn不是虚函数
//D1继承了Base::fcn()虚函数的定义
int fcn(int); //形参列表与Base中的fcn不一致
virtual void f2(); //定义一个新的虚函数,它在Base中不存在
};
class D2 : public D1{
public :
int fcn(int); //是一个非虚函数,隐藏了D1::fcn(int)
int fcn(); //覆盖了虚函数Base::fcn()
void f2(); //覆盖了虚函数f2
};
//调用虚函数的例子
//fcn是Base中的虚函数
//D1直接继承Base的虚函数fcn
//D2重载了Base的fcn
Base bobj;
D1 d1obj;
D2 d2obj;
Base *bp1 = &bobj, *bp2 = &d1jobj, *bp3 = &d2obj;
bp1->fcn(); //虚调用:运行时执行Base::fcn
bp2->fcn(); //虚调用:运行时执行Base::fcn
bp3->fcn(); //虚调用:运行时执行D2::fcn
//f2是D1中的虚函数
//Base中没有定义f2
//D2重载了D1的虚函数f2
D1 *d1p = &d1obj;
D2 *d2p = &d2obj;
bp2->f2(); //错误:Base对象中没有名为f2的成员
d1p->f2(); //虚调用:执行D1::f2()
d2p->f2(); //虚调用:执行D2::f2()
//调用非虚函数的例子
//fcn(int)是D1中的 非虚函数
//Base中没有定义fcn(int)
//D2中的fcn(int)隐藏了D1中的fcn(int)
Base *p1 = &d2obj; //d2obj是D2类型的对象
D1 *p2 = &d2obj;
D2 *p3 = &d2obj;
p1->fcn(42); //错误:Base中没有接受int的fcn
p2->fcn(42); //静态绑定:调用D1::fcn(int)
p3->fcn(42); //静态绑定:调用D2::fcn(int)
C++ 派生类函数重载与虚函数继承详解的更多相关文章
- C#虚函数virtual详解
在面向对象编程中,有两种截然不同的继承方式:实现继承和接口继承.在实现继承时候,在Java中,所有函数默认都是virtual的,而在C#中所有函数并不默认为virtual的,但可以在基类中通过声明关键 ...
- C++:虚函数的详解
5.4.2 虚函数详解 1.虚函数的定义 虚函数就是在基类中被关键字virtual说明,并在派生类重新定义的函数.虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问 ...
- c++运算符重载和虚函数
运算符重载与虚函数 单目运算符 接下来都以AClass作为一个类例子介绍 AClass{ int var } 区分后置++与前置++ AClass operator ++ () ++前置 一般设计为返 ...
- C#虚方法virtual详解
转: http://www.cnblogs.com/jason_yjau/archive/2009/08/25/1553949.html C#虚方法virtual详解 在C++.Java等众多OOP语 ...
- 4.C#虚方法virtual详解
C#虚方法virtual详解 在C++.Java等众多OOP语言里都可以看到virtual的身影,而C#作为一个完全面向对象的语言当然也不例外. 虚拟函数从C#的程序编译的角度来看,它和其它一般的函数 ...
- 虚方法virtual详解
虚方法virtual详解 从C#的程序编译的角度来看,它和其它一般的函数有什么区别呢?一般函数在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,也就是写死了的!而虚函数在 ...
- 「万字图文」史上最姨母级Java继承详解
摘要:继承是面向对象软件技术中的一个概念.它使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用. 本文分享自华为云社区<「万字图文」史上最姨母级Java继承详解丨[奔跑吧!JAVA] ...
- [原创]JavaScript继承详解
原文链接:http://www.cnblogs.com/sanshi/archive/2009/07/08/1519036.html 面向对象与基于对象 几乎每个开发人员都有面向对象语言(比如C++. ...
- 转载 LayoutInflater的inflate函数用法详解
http://www.open-open.com/lib/view/open1328837587484.html LayoutInflater的inflate函数用法详解 LayoutInflater ...
随机推荐
- tomcat容器启动失败疑难问题解决方案
严重: 子容器启动失败java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: 初始化组件[or ...
- 【刷题-PAT】A1135 Is It A Red-Black Tree (30 分)
1135 Is It A Red-Black Tree (30 分) There is a kind of balanced binary search tree named red-black tr ...
- Cesium源码剖析---Ambient Occlusion(环境光遮蔽)
Ambient Occlusion简称AO,中文没有太确定的叫法,一般译作环境光遮蔽.百度百科上对AO的解释是这样的:AO是来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果,可以解决或改善漏光 ...
- 搭建服务器之FTP
FTP服务器,使用软件vsftpd,服务守护进程也是vsftpd.客户端访问的话可以用浏览器或ftp命令行. 1.yum install vsftpd.安装简单主要是配置,这个比httpd复杂点的地方 ...
- 哪些是GET请求,哪些是POST请求
GET请求: 1,form标签 method=get 2,a标签 3,link标签引入css 4,Script标签引入js文件 5,img标签引入图片 6,iframe引入html页面 7,在浏览器地 ...
- 集合框架-工具类-Collections-其他方法将非同步集合转成同步集合的方法
集合框架TXT Collections-其他方法将非同步集合转成同步集合的方法
- Telegra.ph | 简洁的文章发布平台
https://telegra.ph 自由 Telegraph 并不强调内容管理方这一概念,真正做到了「人人都是媒体」.通过 Telegraph 发布的文章,理论上来说不会存在删除的危险,并且由于会产 ...
- Java 实现订单未支付超时自动取消
在电商上购买商品后,如果在下单而又没有支付的情况下,一般提示30分钟完成支付,否则订单自动.比如在京东下单为完成支付: 超过24小时,就会自动取消订单,下面使用 Java 定时器实现超时取消订单功能. ...
- .Net Api 之如何使用Elasticsearch存储文档
.Net Api 之如何使用Elasticsearch存储文档 什么是Elasticsearch? Elasticsearch 是一个分布式.高扩展.高实时的搜索与数据分析引擎.它能很方便的使大量数据 ...
- 使用Termux并与ubuntu建立ssh连接
什么是Termux? Termux是一个Android终端仿真器和Linux环境应用程序,直接工作,无需根目录或设置.一个最小的基本系统被自动安装-额外的软件包可以使用APT软件包管理器来使用.不需要 ...