最近碰到一些问题,一开始很难调试和解决,最后发现原来是在基类函数的模板方法中对子类需要重写的函数没有使用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. ♫【JS模式】偏函数

    <深入浅出Node.js> var toString = Object.prototype.toString var isType = function(type) { return fu ...

  2. 【动态规划】HDU 5791 Two

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5791 题目大意: A,B两个数列,问A的子集和B的子集相等的子集对数.子集内顺序按照数列顺序,相同的 ...

  3. tyvj P1517 飘飘乎居士的乌龟(最大流)

    P1517 飘飘乎居士的乌龟 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 飘飘乎居士养了乌龟.当然,这些乌龟是用来出售赚取利润的. 描述 飘飘乎居士的乌龟 ...

  4. Java中的数据类型及相互转换方法

    本文主要讲解两个部分: 一.Java中的数据类型有哪些? 二.数字类型和字符串类型相互转换的方法? 一.Java中的数据类型有哪些: Java中的数据类型有:基本数据类型和引用数据类型: 基本数据类型 ...

  5. Jenkins 二:邮件配置

    默认邮件的配置 假设管理员邮箱是 user1@domain1.com,密码是pw1. 1. 打开“系统管理”-> “系统设置”. 2. 找到“Jenkins Location”-> “系统 ...

  6. Girls' research - HDU 3294 (Manacher处理回文串)

    题目大意:给以一个字符串,求出来这个字符串的最长回文串,不过这个字符串不是原串,而是转换过的,转换的原则就是先给一个字符 例如 'b' 意思就是字符把字符b转换成字符 a,那么c->b, d-& ...

  7. kafka单机安装配置

    1.下载kafka wget https://www.apache.org/dyn/closer.cgi?path=/kafka/0.8.2.1/kafka_2.9.2-0.8.2.1.tgz 2.解 ...

  8. 快速查询本机IP 分类: windows常用小技巧 2014-04-15 09:28 138人阅读 评论(0) 收藏

    第一步: 点击windows建(屏幕左下方),在搜索程序和文件文本框内输入:cmd 第二步:      点击Enter建进入. 第三步: 输入:ipconfig即可. 版权声明:本文为博主原创文章,未 ...

  9. mongoDb 给表添加+ 删除字段

    1 .添加一个字段.  url 代表表名 , 添加字段 content. 字符串类型. db.url.update({}, {$set: {content:""}}, {multi ...

  10. Java model 对象处理

    public class BaseFlowBo { /** * 流量 */ private String flow; /** * Pv */ private String pv; /** * CTR ...