C++函数覆盖的思考
最近碰到一些问题,一开始很难调试和解决,最后发现原来是在基类函数的模板方法中对子类需要重写的函数没有使用virtual,如下
class Base
{
public:
void say(){test();}
void test(){}
}; class Child : public Base
{
public:
void test(){}
};
准备用Template Method的我很有自信的敲着自己的代码,期待在
Child a;
a.say();
的时候,a会去最终调用Child的test。注意,这里面没有牵扯到任何基类指针乃至基类引用指向子类的概念,纯粹是一个Child实体,我一厢情愿的以为a的say在进入Base的say之后,会去调用Child的test.因为它本身就是Child嘛~但就是这么个很小的细节,让我的问题在出现的时候我很难理解。真实的情况是此时的test实际调用的是Base的test。但如果我们换个写法
class Base
{
public:
void say(){test();}
virtual void test(){}
}; class Child : public Base
{
public:
virtual void test(){}
};
此时a.test会去最终调用Child的test。为什么,这里面根本就没有基类指针或者基类引用去指向子类的过程啊!虚函数的实现不是一定要通过指针或者引用吗?纯粹这样的实体类为什么也会采取动态绑定?
其实这也只能怪自己读书太肤浅,一直天真的以为动态绑定的实现必须满足两个先天条件。即virtual关键字的满足和基类指针和引用的满足。其实我们错了,C++中对应virtual机制来说,不过你是不是指针或者引用,只要你是Child实体并且满足virtual重写父类,那么无论如何,都会进入到你自己的函数当中去。因此,当我们要实现设计模式中模板方法模式的时候,不要怀疑和犹豫我此时需要子类实现的函数要不要虚函数,因为答案一定是一定要。你可以考虑这样的情况,其实只有两种方法会真正用到子类,一种是基类指针或者引用,一种就是子类实体本身,结合上文案例,不管这两种情况你采用哪种,当对应到test进行选择的时候,如果你用virtual都会根据最终的实际类型去选择,这就是区别。
再比如,当我们要抽象A与B提炼公共类C的时候,其实不需要考虑到底外部会不会对C进行调用,就算没有对C进行引用,一旦你做了公共分离,你就会在公共代码中去调用你子类的函数,此时你的公共类中必然有这个函数的空定义或者接口,你可以考虑如果不进行virtual修饰那情况会是什么样。比如
class C
{
public:
void say() {test();}
void test();
}; class A
{
public:
void test();
}; class B
{
public:
void test();
};
你将公共代码的say进行提炼到父类C的时候,当外界对你的A进行a.say(),自然而然会去调用C的say,但这个时候,里面的test要注意不是virtual,为什么会出现这种选择?因为我们在做抽象分离的时候确实会去考虑这个问题----倘若外界没有对我公共接口的访问,我还需要虚函数吗?
答案很明显已经出来了,结合上文,即使你外界没有对你的C进行任何接口调用,你的A或者B都是写死的,比如A a; B c;没有 C *a = new A等,你也务必需要在提取公共类的时候显示加上virtual
class C
{
public:
void say() {test();}
virtual void test();
}; class A
{
public:
virtual void test();
}; class B
{
public:
virtual void test();
};
因为只有这样,外界对A进行say访问的时候,最终的test才会调到它自己,否则,就会去调C的test。
总结,归根结底,C++的virtual并不是说一定要和基类指针或者引用挂钩才会起作用(其实很多介绍多态的书在举例子的时候大多采用这种写法因此会给我们带来一定误导,让我们始终认为这两个条件的必要性),其实哪怕只是实例,也是需要将virtual考虑在内的。
C++函数覆盖的思考的更多相关文章
- C++中函数重载和函数覆盖的区别
C++中经常会用到函数的重载和覆盖,二者也在很多场合都拿出来进行比较,这里我就对二者的区别做点总结: 函数重载: 函数重载指的是函数名相同.函数特征值不同的一些函数,这里函数的特征值指的是函数的参数的 ...
- C++ ------ 虚函数覆盖、重载
在C++语言中,虚函数是非常重要的概念,虚函数是实现C++面向对象中多态性和继承性的基石.而多态性和继承性则是面向对象语言的精髓.掌握虚函数才算是真正掌握C++语言,而C++语言中虚函数的继承覆盖与函 ...
- 关于JavaScript预编译和执行顺序以及函数引用类型的思考
昨晚在对项目中的一部分做模块化处理的时候,遇到了一个问题,一个重新定义的function对一个通用类中的function进行赋值覆盖的时候,失败了.问题抽象出来是这样的: <script > ...
- 由项目中一个hash2int函数引发的思考
hash2int /** * 计算一个字符串的md5折算成int返回 * @param type $str * @return type */ function hash2int($str) { $m ...
- 关于Linux系统basename函数缺陷的思考
某模块作为前台进程独立运行时,运行命令携带命令行参数:作为某平台下守护进程子进程运行时,需要将命令行参数固化在代码里.类似如下写法: char *argv[] = {"./DslDriver ...
- java中的异常处理机制_函数覆盖时的异常特点
/*注意:异常声明在函数上 异常在子父类覆盖时的体现1.子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者异常的子类2.如果父类方法抛出多个异常,那么子类在覆盖该方法 ...
- C++多态实现(虚函数,成员函数覆盖、隐藏)
// 1.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> using namespace ...
- C# 函数覆盖总结学习
覆盖类成员:通过new关键字修饰虚函数表示覆盖该虚函数.一个虚函数被覆盖后,任何父类变量都不能访问该虚函数的具体实现.public virtual void IntroduceMyself(){... ...
- linux 动态库 静态库 函数覆盖
本文讨论了linux动态库 静态库中函数的覆盖问题. 测试目的: 同名函数,分别打成动态库libdync_lib.so与静态库libstatic_lib.a,并把libstatic_lib.a打到另 ...
随机推荐
- Windows下Vundle插件BundleSearch命令出现错误解决方案
纯粹参考他人博文,已证明解决方案正确,转载时请注明参考链接!!! 参考链接: http://www.tuicool.com/articles/e2aiEn 在Windows下使用Vim时,用Vundl ...
- AlgorithmsI PA2: Randomized Queues and Deques Deque
本次作业考察利用array 或者linked list 实现规定时间复杂度的queue 和stack, 不能使用java 自带的stack 和queue. 目的是让我们掌握自己设计的函数的复杂度. D ...
- 1710: [Usaco2007 Open]Cheappal 廉价回文
Description 为 了跟踪所有的牛,农夫JOHN在农场上装了一套自动系统. 他给了每一个头牛一个电子牌号 当牛走过这个系统时,牛的名字将被自动读入. 每一头牛的电子名字是一个长度为M (1 & ...
- 【转】Android Recovery模式
原文网址:http://leox.iteye.com/blog/975303 (muddogxp 原创,转载请注明) Recovery简介 Android利用Recovery模式,进行恢复出厂设置,O ...
- 数据结构(二维线段树,差分): NOI2012 魔幻棋盘
貌似想复杂了…… #include <iostream> #include <cstring> #include <cstdio> #define mid ((l+ ...
- linux内网机器访问外网代理设置squid
公司一般出于安全考虑, 在同一局域网中只有一台机器可以访问外网,运维进行了整体的限制, 但是在后面的工作中,需要在机器上安装一些软件,及命令,所以其他的机器需要访问外网来简化工作, 但又不能打乱原有运 ...
- Timeout for tests
如果想把timeout太久的测试自动标记为失败,有两种方法: 1.在 @Test里加上 Timeout 参数 定义"timeout=1000“的话,如果超过1000 毫秒,failure会被 ...
- WebSphere应用服务器中https 请求协议的相关注意事项(服务器使用代理上Internet)
最近遇到个需求需要web服务器应用通过https方式请求外部Internet服务器的接口,一开始本地测试时使用以下代码: String businessCode = "SH30580&quo ...
- 记录下mybatis中#{}和${}传参的区别
最近在用mybatis,之前用过ibatis,总体来说差不多,不过还是遇到了不少问题,再次记录下, 比如说用#{},和 ${}传参的区别, 使用#传入参数是,sql语句解析是会加上"&quo ...
- Team Foundation Server 2013 with Update 3 Install LOG
[Info @10:14:58.155] ====================================================================[Info @ ...