纯虚拟函数的存在 & 虚拟规格的存在, 及其中 const 的存在

考察以下代码:

class AbstractBase
{
public:
    virtual IBase() = 0;
    virtual Interface() const = 0;
    virtual const char*
        Member() const {return _member;}
    protected:
        char *_member;
}

这段代码有个问题, 虽说这个类是个抽象的, 但它仍然需要一个构造函数来初始化 _member.
可能有人觉得, 这个 AbstractBase 的设计者试图让每一个它的 derived class 提供 _member的初值. 但是如果是这样, derived class 的唯一要求就是 AbstractBase 必须提供一个带有唯一参数 protected constructor:

AbstractBase::
AbstractBase(char *member_value = 0)
    :_member(member_value){}

一般而言, class 的data member 应该被初始化, 并且只是在 constructor 中或是在 class member functions 中指定初值. 其他任何操作都将破坏封装性质, 使 class 的维护和修改更加困难.
当然你也可能说设计者的错误不在于未提供一个explicit constructor 而是他不应该在一个 抽象的 base class 中声明 data members. 接口的思想固然没错, 但是把共享的数据提出放在 base class 里也是名正言顺的.

C++ 新手(比如我) 常常很惊讶的发现, 一个人竟然可以定义和调用一个 pure virtual function; 不过它们只能被静态调用, 不能经由虚拟机制调用. 例如以下代码是合法的:

inline void
AbstractBase::Interface() const
{
    //定义之前的纯虚函数 Interface() const
} 

inline void
ConcereteDerived::interface() const
{
    //调用一个纯虚函数
    AbstractBase::Interface();
}

具体要不要这么做, 取决于 class 的设计者,. 唯一例外的就是 pure virtual destructor; class 的设计者一定要定义它. 因为每一个 derived class destructor 都会被编译器扩展, 以静态调用的方式调用其每一个 virtual base ckass 以及上一层 base class 的 destructor. 因此只要少了一个就会导致链接失败.

PS. 我用的 VC++ 12.0 编译器显示的是:
error LNK2019: 无法解析的外部符号 "public: virtual void __thiscall
和 fatal error LNK1120: 1 个无法解析的外部命令;

在 gcc 4.8.1 上显示错误是:
undefined reference to `IBase::Interface() const‘
这个...我很期待使用 Clang 或 icc 的朋友可以编译这段代码看下能不能成功...
但是段代码 Lippman 说是合法的, 我姑且就信了吧.

有人可能觉得, 对一个 pure virtual destructor 的调用操作不是应该在编译器扩展 derived class的额 destructor 的时候压抑下来吗? 不, class 设计者可能已经真的定义了一个 pure virtual destructor, 这样的设计是以 C++ 语言的一个保证为前提: 集成体系中的每一个 class object 的 destructor 都会被调用, 所以编译器不能压抑这个操作.

你也可能觉得, 编译器会不会合成一个 pure virtual destructor 的函数定义呢? 可惜编译器不行啊, 因为它对一个执行文件采取的是分离编译模型, 开发环境可以提供一个设备, 在链接时找出 pure virtual destructor 不存在的事实体, 然后重新激活编译器, 赋予一个特殊指令, 当然目前是否有编译器真这么做还不知道.

虚拟规格的存在
如果咱们真的把 AbstractBase::Member() 设计为一个 virtual function, 那将是一个糟糕的选择, 因为其函数定义并不与类型有关, 所以不会被后继的 derived class 改写. 此外, 又因为它的 non-virtual 函数实体是一个 inline 函数, 如果常常被调用的话, 效率上的报应可是不轻.

那么编译器能不能分析出该函数实际上只有一个实体存在于 class 层次体系中? 如果可以, 能不能把调用操作转换成一个静态调用操作, 以允许该调用操作的 inline expansion? 如果 class 层次体系陆续被加入新的 classes, 带有这个函数的新实体, 又当如何? 没错, 新的 class 会破坏优化! 该函数会被重新编译(或是产生第二个实体, 编译器决定那个被调用). 但是, 欲挖出这样的相依性, 有可能需要某种形式的 persistent program database 或 libray manager.

所以说, 把所有的成员函数都声明为virtual function, 然后再靠编译器的优化操作把非必要的 virtual invocation 去除, 并不是个好主意.
虚拟规格中 const 的存在

决定一个 virtual function 是否需要 const 似乎是一个琐碎的事, 但当你真的认真考虑的时候, 却不容易做决定. 做这件事情, 意味着要假设 subclass 实体可能会被无穷次的使用, 不把函数声明为 const, 又意味着该函数不能够获得一个 const reference 或 const pointer.更恶心的是, 声明一个函数为 const 之后才发现实际上其 derived instance 必须修改一个 data member...
所以给的建议就是, 不要给 virtual function 加个 const.

结论:
靠谱的类是避免头疼的保证:

class AbstractBase
{
public:
    virtual ~AbstractBase() = default;
    virtual void  Interface() = 0;
    const   char* Member() const {return _member;}    //就一个实体你跟人家学什么虚拟?
protected:
    Abstractbase(char *member = 0):_member(){}        //一个带参数的 constructor
    char *_member;
};
时间: 2024-10-09 07:20:55

纯虚拟函数的存在 & 虚拟规格的存在, 及其中 const 的存在的相关文章

C++虚拟函数

一 为了方便使用多态特性,常常需要在基类中定义虚拟函数. 在很多情况下,积累本身生成对象是不合理的.例如,动物作为一个基类可以派生出猫.狗.猴子.熊.牛等子类,但动物本身生成对象明显不合常理. 为了解决上述问题,引入了纯虚函数的概念,江汉数定义为纯虚函数,编译器要求在派生类中必须予以重载以实现多态性. 同时,含有纯虚函数的类为抽象类,他不能生成对象. 纯虚函数的定义格式: virtual 类型 函数名=0; 二 测试代码如下: 1 #include <iostream> 2 3 using n

More Effective C++----(24)理解虚拟函数、多继承、虚继承和RTTI所需的代价

Item M24:理解虚拟函数.多继承.虚继承和RTTI所需的代价 C++编译器们必须实现语言的每一个特性.这些实现的细节当然是由编译器来决定的,并且不同的编译器有不同的方法实现语言的特性.在多数情况下,你不用关心这些事情.然而有些特性的实现对对象大小和其成员函数执行速度有很大的影响,所以对于这些特性有一个基本的了解,知道编译器可能在背后做了些什么,就显得很重要.这种特性中最重要的例子是虚拟函数. 当调用一个虚拟函数时,被执行的代码必须与调用函数的对象的动态类型相一致:指向对象的指针或引用的类型

学习笔记之深入浅出MFC 第8章 C++重要性质----虚拟函数与多态(Polymorphism)

1.虚拟函数的由来 上面我们曾经提过一个例子: CShape shapes[5]; . . . //令5个shapes各为矩形.正方形.椭圆形.圆形.三角形 for ( int i = 0;  i<5;  i++) { shapes[i].display(); } 在上一节中我们说这种一般化的操作无法完成.你还记得为什么吗?是这样的,上面一节中讲到,由于每一个子类图形的绘制不同,所以display()各不相同,所以无法提升到基类中去.那么用基类定义的shapes[]数组,当然也就没有displa

含有虚函数菱形的虚拟继承(没有对虚函数进行重写)

在VS2013编程,调试 问题 :  菱形继承会引来,二义性 1.源代码 </pre><pre name="code" class="cpp">#include <iostream> using namespace std; class Base { public: virtual void FunTest() { cout << "Base::FunTest () " << endl;

C++ 实用泛型编程之 虚拟函数(C++ virtual function)杂谈

一 C++虚拟函数(C++ virtual function)杂谈 我们在编程的时候,经常会遇到这样的情况,假设有两个对象,你要在函数中分别调用它们的OnDraw方法,我们以前的做法一般是这样的. void f(int iType) { switch(iType) { case 1: //CCircle OnDraw break; case 2: //CRectangle OnDraw break; } } 这种方法当然能解决我们的问题,但是如果有新的类型要增加,它就必须要往下加代码才行了,这样

C++:抽象基类和纯虚函数的理解

转载地址:http://blog.csdn.net/acs713/article/details/7352440 抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层. ⑴抽象类的定义: 称带有纯虚函数的类为抽象类. ⑵抽象类的作用: 抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作.所以派生类实际上刻画了一组子类的操作接口的通用语义,这些语义也传给子类,子类可以具体实现这些

C++ 虚函数与纯虚函数 浅析

[摘要] 本文首先简述虚函数与纯虚函数的定义,然后分析比较两者的区别与联系(DWS). [正文] 1)虚函数与纯虚函数有什么区别? 虚函数,不代表函数为不被实现的函数,为了允许用基类的指针来调用子类的这个函数:允许被其子类重新定义的成员函数. 纯虚函数,才代表函数没有被实现,为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数. 2)虚就虚在所谓"推迟联编"或者"动态联编"上,一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的.

C++虚函数和纯虚函数

只有用virtual声明类的成员函数,使之成为虚函数,不能将类外的普通函数声明为虚函数.因为虚函数的作用是允许在派生类中对基类的虚函数重新定义.所以虚函数只能用于类的继承层次结构中. 一个成员函数被声明为虚函数后,在同一类族中的类就不能再定义一个非virtual的但与该虚函数具有相同的参数(包括个数和类型)和函数返回值类型的同名函数. 根据什么考虑是否把一个成员函数声明为虚函数? ①  看成员函数所在的类是否会作为基类 ② 看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该

C++纯虚函数、虚函数、实函数、抽象类,重载、重写、重定义

首先,面向对象程序设计(object-oriented programming)的核心思想是数据抽象.继承.动态绑定.通过数据抽象,可以使类的接口与实现分离,使用继承,可以更容易地定义与其他类相似但不完全相同的新类,使用动态绑定,可以在一定程度上忽略相似类的区别,而以统一的方式使用它们的对象. 虚函数的作用是实现多态性(Polymorphism),多态性是将接口与实现进行分离,采用共同的方法,但因个体差异而采用不同的策略.纯虚函数则是一种特殊的虚函数.虚函数联系到多态,多态联系到继承. 一.虚函