最近碰到一些问题,一开始很难调试和解决,最后发现原来是在基类函数的模板方法中对子类需要重写的函数没有使用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++函数覆盖的思考的更多相关文章

  1. C++中函数重载和函数覆盖的区别

    C++中经常会用到函数的重载和覆盖,二者也在很多场合都拿出来进行比较,这里我就对二者的区别做点总结: 函数重载: 函数重载指的是函数名相同.函数特征值不同的一些函数,这里函数的特征值指的是函数的参数的 ...

  2. C++ ------ 虚函数覆盖、重载

    在C++语言中,虚函数是非常重要的概念,虚函数是实现C++面向对象中多态性和继承性的基石.而多态性和继承性则是面向对象语言的精髓.掌握虚函数才算是真正掌握C++语言,而C++语言中虚函数的继承覆盖与函 ...

  3. 关于JavaScript预编译和执行顺序以及函数引用类型的思考

    昨晚在对项目中的一部分做模块化处理的时候,遇到了一个问题,一个重新定义的function对一个通用类中的function进行赋值覆盖的时候,失败了.问题抽象出来是这样的: <script > ...

  4. 由项目中一个hash2int函数引发的思考

    hash2int /** * 计算一个字符串的md5折算成int返回 * @param type $str * @return type */ function hash2int($str) { $m ...

  5. 关于Linux系统basename函数缺陷的思考

    某模块作为前台进程独立运行时,运行命令携带命令行参数:作为某平台下守护进程子进程运行时,需要将命令行参数固化在代码里.类似如下写法: char *argv[] = {"./DslDriver ...

  6. java中的异常处理机制_函数覆盖时的异常特点

    /*注意:异常声明在函数上 异常在子父类覆盖时的体现1.子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者异常的子类2.如果父类方法抛出多个异常,那么子类在覆盖该方法 ...

  7. C++多态实现(虚函数,成员函数覆盖、隐藏)

    // 1.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> using namespace ...

  8. C# 函数覆盖总结学习

    覆盖类成员:通过new关键字修饰虚函数表示覆盖该虚函数.一个虚函数被覆盖后,任何父类变量都不能访问该虚函数的具体实现.public virtual void IntroduceMyself(){... ...

  9. linux 动态库 静态库 函数覆盖

    本文讨论了linux动态库  静态库中函数的覆盖问题. 测试目的: 同名函数,分别打成动态库libdync_lib.so与静态库libstatic_lib.a,并把libstatic_lib.a打到另 ...

随机推荐

  1. VS2010中编写宏添加作者信息与函数注释

    这里所说的宏是指通过一系列键盘组合键和可以插入自定义内容.下面介绍怎么编写一个自己的宏: 1.在Visual Studio 2010中按Alt+F11打开宏IDE: 2.打开后选择添加模块: 3.在弹 ...

  2. 学习C++所需看的书和顺序

    初学: <C++ 编程思想> <C++ Primer><The C++ Programming Language> 提高: <C++ 的发展与演化> & ...

  3. sql按照in中的顺序进行排序 mysql

    经测试以下三种情况,都可以. SELECT a.id,a.name as goods_name,a.logoimg,b.name as store_name FROM sh_goods a LEFT ...

  4. 【转】Android Recovery模式

    原文网址:http://leox.iteye.com/blog/975303 (muddogxp 原创,转载请注明) Recovery简介 Android利用Recovery模式,进行恢复出厂设置,O ...

  5. Ubuntu 12.04 下安装 Eclipse

    方法一:(缺点是安装时附加openjdk等大量程序并无法去除,优点是安装简单) $ sudo apt-get install eclipse 方法二:(优点是安装内容清爽,缺点是配置麻烦)1.安装JD ...

  6. 数据结构(线段树):BZOJ 1568 [JSOI2008]Blue Mary开公司

    1568: [JSOI2008]Blue Mary开公司 Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 602  Solved: 214[Submit ...

  7. hdu 4619 二分图最大匹配 ——最大独立集

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4619 #include <cstdio> #include <cmath> # ...

  8. vue-cli 中 使用vue-resource 输出后台数据

    阅读此文前,请了解vue-cli 组件如何使用  http://www.cnblogs.com/pearl07/p/6252116.html 1,mock(模拟)后台数据(新建data.Json文件) ...

  9. oracle 大文本由clob来存

    file字段是varchar2类型(最多只能存储4000字符),空间不够用了,因此将其改为clob类型(支持4G存储量). 但是如果该字段中已经有数据,是不为空的,直接用语句改,会报错. 那么需要借助 ...

  10. [置顶] cocos2d-x 植物大战僵尸(4) 帽子僵尸的产生

         大家早上好,趁着阳光美好的时候,我打算写下博客:今天要说的是僵尸的产生了,这块和太阳因子的产生比较相似,大体上的区别在于僵尸的基类这块:我在考虑是详细的写还是大体的写,本着对自己作业的态度和 ...