在C++的程序设计中有一些设计开发的典型技巧需要整理讨论,在此抛砖引玉,为自己做积累,请高人也多多指教。

1.简介

在标准C++库中我们可以看到这样的一个现象:

6个公有虚函数,并且都是std::exception::what()和其重载。

142个非公有虚函数。

这样设计的目的何在呢,为什么“多此一举”的把虚函数设置为非公有呢?

这就是NVI机制要求的:将虚函数声明为非公有,而将公有函数都声明为非虚——虚拟和公有选其一。

2.机制分析

程序员常常将基类中的虚函数公有化,来提供一个接口的定义(virtual的功劳)同时提供其实现(具体的一个实现)。

class Base{public:virtual void Foo(int){cout<< "Base's Foo!" << endl;};
};

问题就出在“同时”——一个定义了接口的形式,一个定义了默认的一个实现,显然这样的设计没有将接口定义和实现分来。在这个时候,我们可以使用模板方法模式的思想:

class Base{public:void Foo(){DoFoo1();DoFoo2();}//use DoFooX()private:virtual void DoFoo1(){cout << "Base's DoFoo1" <<endl;}virtual void DoFoo2(){cout << "Base's DoFoo2" <<endl;}};class Derived: public Base{private:virtual void DoFoo1(){cout << "Derived's DoFoo1" << endl;};
};

函数Foo定义了接口的形式,而DoFooX()函数则实现了对Foo函数的行为定制,实现了接口定义和实现的分离,我们举一个例子来说明好处:如果我们希望在Foo中做一下CS(Critical Section)的加锁解锁控制:

若我们完成这样的接口与实现分离,那么我们的实现是在基类的接口处添加所需流程即可,子类不需要修改:

class Base{public:void Foo(){cout << "Locking" << endl;DoFoo1();DoFoo2();cout << "Unlocking" << endl;}//use DoFooX()private:virtual void DoFoo1(){cout << "Base's DoFoo1" <<endl;}virtual void DoFoo2(){cout << "Base's DoFoo2" <<endl;}};class Derived: public Base{private:virtual void DoFoo1(){cout << "Derived's DoFoo1" << endl;};};

若不实现接口与实现分离,则从基类到子类都需要修改:

class Base{public:virtual void Foo(){cout << "Locking" << endl;cout << "Base's Foo" << endl;cout << "Unlocking" << endl;}};class Derived: public Base{public:virtual void Foo(){cout << "Locking" << endl;cout << "Derived's Foo" << endl;cout << "Unlocking" << endl;};};

注意,当且仅当子类需要调用基类的虚函数时才将虚函数设置为protected(否则没有权限),并且NVI机制不适用于析构函数,对于析构函数,如果设为公有则应该设置为虚拟(在允许多态删除的基类中),否则设置为私有或者protected的非虚拟形式(不含多态删除的基类中)。

带来的风险:

首先是FBC问题(Fragile Base Class ),下边是一个例子:

class Set {std::set<int> s_;public:void add (int i) {s_.insert (i);add_impl (i); // Note virtual call.}void addAll (int * begin, int * end) {s_.insert (begin, end);   //  --------- (1)addAll_impl (begin, end); // Note virtual call.}private:virtual void add_impl (int i) = 0;virtual void addAll_impl (int * begin, int * end) = 0;
};
class CountingSet : public Set {private:int count_;virtual void add_impl (int i) {count_++;}virtual void addAll_impl (int * begin, int * end) {count_ += std::distance(begin,end);}
};

如果此时我们在父类中修改了addAll函数,改为将从begin到end的数字都调用一遍add函数,那么,子类的功能就紊乱了——子类计数就会多记录一倍(因为在子类中,add_impl每次都会计数一个,并且addAll_impl也会整体计数一次)。所以,为了防止出现FBC,一般一个公有非虚函数调用一个私有虚函数。

其次是性能上的考虑,毕竟多了一层函数调用。对此,参考文献2指出:“a word about efficiency: No, none is lost in practice because if the public function is a one-line passthrough declared inline, all compilers I know of will optimize it away entirely, leaving no overhead. (Indeed, some compilers will always make such a function inline and eliminate it, whether you personally really wanted it to or not, but that’s another story.)”

【C++程序设计技巧】NVI(Non-Virtual Interface )相关推荐

  1. 段式液晶程序设计技巧

    段式液晶程序设计技巧 一.段式液晶的基本参数 1.Duty:占空比 该项参数一般也称为Duty数或者com数,段式液晶一般采用时分动态扫描的驱动模式,此模式下,每个com的有效选通时间与整个扫描周期的 ...

  2. UVM virtual interface errors

    UVM virtual interface errors 1) Error: uninitialized virtual interface object 1) Error: uninitialize ...

  3. UVM virtual interface

    virtual interface 实用总结: ①.interface只能在module中声明,在class中要用virtual interface: ②.virtual interface是在仿真运 ...

  4. 国家二级c语言程序设计技巧,计算机等级考试二级C语言程序设计技巧.doc

    计算机等级考试二级C语言程序设计技巧 在许多应用软件运行时都带有命令行参数,其实这些命令行参数在C语言编写的程序中也可以实现,灵活地运用命令行参数进行处理可以有效地提高程序的运行效率,收到事半功倍的效 ...

  5. System Verilog学习笔记—虚接口(virtual interface)

    1.虚接口(virtual interface) 1.1为什么引入虚接口? 我们知道,通过引入interface可以简化模块儿之间的连接,即interface是连接硬件的,其是硬件语言:但对于验证来说 ...

  6. 华为-vlan间路由之SVI(switch virtual interface)

    SVI(switch virtual interface) (使用多层交换机 配置 vlanif接口) 交换机: vlan batch 10 20 interface GigabitEthernet0 ...

  7. OpenStack Icehouse error: Virtual Interface creation failed解决方法

    最近在ubuntu 14.04上通过devstack搭建OpenStack Icehouse,但是在创建instance时候报错:Virtual Interface creation failed,详 ...

  8. 070_《Delphi7程序设计技巧与实例》

    <Delphi7程序设计技巧与实例> Delphi 教程 系列书籍 (070) <Delphi7程序设计技巧与实例> 网友(邦)整理 EMail: shuaihj@163.co ...

  9. 国家二级c语言程序设计技巧,国家二级C语言机考程序设计题技巧.doc

    计算机国家二级C机考程序设计题技巧 双击桌面上的,启动计算机等级考试二级模拟软件:启动软件后出现如下界面,选择练习模式后确定: 之后出现下面窗口,选择一套题,我们提供的模拟软件中共提供了50套模拟题目 ...

  10. Windows 程序设计技巧

    #include<iostream> #include<windows.h>using namespace std;int main() {int nSelect = ::Me ...

最新文章

  1. 中科院博士返乡卖汉服:3个月卖三百万,高定一件3.5万
  2. 分布式存储系统的关键技术-存储层级内的优化技术
  3. Ubuntu中防火墙设置
  4. c#后台修改前台DOM的css属性
  5. 多处理与线程Python
  6. Adams中的阻尼比样条设置
  7. C++接口工程实践:有哪些实现方法?
  8. pads layout 中 hatch和flood之区别
  9. Linux以及各大发行版介绍
  10. python判断是不是整数1002python判断是不是整数_Python判断一个数是不是为整数的方法...
  11. 源码分析Dubbo前置篇-寻找注册中心、服务提供者、服务消费者功能入口
  12. mysql典型安装和自定义_Mysql8.0.19下载安装—windows版本自定义安装
  13. 购物车demo(内含bug)
  14. python中出现iterator should return strings, not bytes怎么解决
  15. 4月份西部数码.wang域名注册量报告:增速严重缩水
  16. Mac版本Jmeter下载安装教程
  17. scratch小游戏2048
  18. BokTalk块说全球首款基于区块链技术的即时通讯系统
  19. 香港马市、田忌赛马?这款游戏 IP 碉堡了
  20. 微信公共号推广技巧、快速涨粉丝的7大技巧总结

热门文章

  1. Fresher玩深沉
  2. OBS,vMIX,Wirecast,TCliveSP直播串流导播软件区别及比较
  3. 环境经济:上市公司环保支出(2000-2020)104城-城投债数据(2000-2020)
  4. 使用python批量下载ensembl数据库指定类型的文件
  5. 【华为社招OD笔试题】输入一串数字,给出他们的最小的和的绝对值,输出a,b和a+b的绝对值 其中数字范围为[-63500,63500]
  6. Leetcode——714. Best Time to Buy and Sell Stock with Transaction Fee
  7. MySQL的基本操作(五)
  8. 请冷静地对待手中的EOS——EOS数据分析
  9. cjavapy在线正则表达式测试工具
  10. Event Handing guide for iOS