最近碰到一些问题,一开始很难调试和解决,最后发现原来是在基类函数的模板方法中对子类需要重写的函数没有使用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. 【转】Android实例剖析笔记(二)--用实例讲解Andriod的开发过程,以NotesList为实例介绍Android的菜单机制

    原文网址:http://kb.cnblogs.com/page/78304/ 简介 android提供了三种菜单类型,分别为options menu,context menu,sub menu. op ...

  2. Delphi NativeXml读取中文乱码问题解决

    NativeXml默认的字符类型为Utf8String,有时在读取中文时还是会出现乱码问题,在329版本中提供一种类型转换函数sdUtf8ToWide(),我们可以这样sdUtf8ToWide(AXm ...

  3. POJ 3280 Cheapest Palindrome(水题)

    题意:给定一个完全由小写字母组成的字符串s,对每个字母比如x(或a,b,c...z),在字符串中添加或者删除它分别需要花费c1['x']和c2['x']的代价,问将给定字符串变成回文串所需要的最少代价 ...

  4. poj1065

    题目大意: 木棍(好吧,承认确实做过这个题,嘎嘎) 有一堆木棍大约有n根,木棍的长度和重量都预先知道,这些木棍会在一个木工机械上一个接一个的处理,这需要一些时间,称为设置时间,为机械准备处理一根木头, ...

  5. docker安装lnmp 环境

    docker基础知识请转 docker中文文档:http://docker-doc.readthedocs.io/zh_CN/latest/index.html docker英文文档: https:/ ...

  6. 安卓在SQLiteOpenHelper类进行版本升级和降级

    一.升级(使用到onUpgrade()方法和onCreate()没有安装过才用到) 简单理一下思路:  v1.0 (也就是说第一次使用这软件,没有安装过 所有在onCreate() 方法里写代码)   ...

  7. MVC 无法将类型“System.Collections.Generic.List<AnonymousType#1>”隐式转换为“System.Collections.Generic.IList<Mvc3Modeltest.Models.Movie>”。存在一个显式转换(是否缺少强制转换?))

    1.问题: 2.解决方案:强制指定类型. 解决之.

  8. [AngularJS] ng-if vs ng-show

    ng-show: ng-show element will stay in dom, just added a ng-hide attr, so it won't show. ng-if: It ha ...

  9. HDU1097 A hard puzzle

    A hard puzzle Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) To ...

  10. UML中的图

    用例图.类图.包图.顺序图.协作图.状态图.活动图.构件图.部署图等 1.用例图 显示多个外部参与者以及他们与系统提供的用例之间的连接.用例是系统中的一个可以描述参与者与系统之间交互作用功能单元.用例 ...