学习C++ 不可避免地会遇到虚函数的问题,下面几个问题在学习初期或多或少会存在一些疑惑,所以便将其总结了下来。

1.为什么静态成员函数、构造函数不能定义为虚函数?

  因为静态成员函数是一个大家共享的一个资源,它其实就是一个“受类域限定符限制”的普通函数,没有this指针,不需要对象就可以调用;而虚函数是实打实的成员函数,调用依赖于创建的实例(编译时要把实例的地址给该成员函数的this指针) 所以一个依赖对象,一个则不,两者是是相矛盾的.
  
  对构造函数来说,因为在用运行构造函数来构造实例的时候,实例还未创建完成,而虚函数的运行是建立在对象构建完备的基础上,所以将构造函数定义为虚函数是行不通的。
 
 

2.为什么在构造、析构函数中不要调用virtual函数?

  直白的说是由于调用的虚函数达不到虚函数的效果,而实现虚函数有代价,结果就是费力不讨好。 

不妨考虑下面一个例子

class Base
{
public:
Base()
{
vfunc();
} virtual void vfunc()
{
cout<<"Base::vfunc()"<<endl;
}
};
class Derive : public Base
{
public:
Derive()
:Base()
,_pData(new int())
{
vfunc();
} virtual void vfunc()
{
cout<<"Derive::vfunc() "<<*_pData<<endl;
} ~Derive() { delete _pData;}
private:
int* _pData;
};
void Test()
{
Derive d;
}

例子

结果是这样: 

 

我们都知道在构造子类对象d的时候,会先去构造d中基类成份,即去调用基类构造函数;但现在这里的虚函数好像构成重写,那么输出怎么不是这样呢?

Derive::vfunc() 
Derive::vfunc()

但是这样答案明显是错的!!前面的这个‘2’不可能出现,它这不还没初始的嘛。

现在发现了问题,拿effectiveC++观点来讲就是“在base class构造期间,virtual函数不是virtual函数”,这里vfunc()没有达到虚函数的效果。

原因就是构造基类成份时d中虚表中尚未注册派生类的VFunc(),这时便只能调用基类的VFunc();所以这类虚函数调用是不会下降到子类中。退一步讲,假使下降到子类中,那也会出现上面那种访问子类中未初始化成员_pData,致使程序崩溃的情况。所以干嘛还要在构造函数当中调用它呢?又没有虚函数效果,还影响效率。
  

对于析构函数,道理同样是如此。派生类部分先析构,然后析构基类部分,但此时只能调用基类自身的函数。倘若一旦让派生类析构函数再执行,对象内派生类成员变量(如_pData)便呈现未定义的值,导致未知的行为。(如果将基类改为纯虚函数,并在构造、析构中调用,此时编译器便不会“安分”了)

所以,不要在构造、析构函数中调虚函数,它带不来预想的结果(多态),就算有也是一张通往“彻夜调试大会串的直达车票”

3.什么情况下要将基类的析构函数声明为虚函数?

  答:用一个基类指针或引用来释放派生类对象时,建议将析构写为虚函数。

  编译器总是根据类型来调用类成员函数。但是一个派生类的指针可以安全地转化为一个基类的指针。这样删除一个基类的指针的时候,C++不管这个指针指向一个 基类对象还是一个派生类的对象,调用的都是基类的析构函数而不是派生类的;所以这样继承类的成分没有被销毁,造成局部销毁对象,形成资源泄漏。

所以建议将析构函数声明为虚函数。这样就实现了多态,避免内存泄漏

      

注:

  ①基类中函数一旦声明为虚函数,不管子类是否加上virtual,子类中形式相同函数继续保持虚函数特性。

  ②析构函数十分特殊,由于基类和子类的析构它们底层其实是同名的(destructor),所以会构成覆盖

虽然虚函数有好处,但C++不
把虚析构函数直接作为默认值
。原因就是是虚函数表的开销以及和C语言的类型的兼容性。有虚函数的对象总是在开始的位置包含一个隐含的虚函数表指针成员。所以,如果我们设计的类不涉及继承关系时,不要将析构搞成虚函数,没有必要。

C++——虚函数问题小集的更多相关文章

  1. C++虚函数和函数指针一起使用

    C++虚函数和函数指针一起使用,写起来有点麻烦. 下面贴出一份示例代码,可作参考.(需要支持C++11编译) #include <stdio.h> #include <list> ...

  2. 匹夫细说C#:从园友留言到动手实现C#虚函数机制

    前言 上一篇文章匹夫通过CIL代码简析了一下C#函数调用的话题.虽然点击进来的童鞋并不如匹夫预料的那么多,但也还是有一些挺有质量的来自园友的回复.这不,就有一个园友提出了这样一个代码,这段代码如果被编 ...

  3. 【C++】多态性(函数重载与虚函数)

    多态性就是同一符号或名字在不同情况下具有不同解释的现象.多态性有两种表现形式: 编译时多态性:同一对象收到相同的消息却产生不同的函数调用,一般通过函数重载来实现,在编译时就实现了绑定,属于静态绑定. ...

  4. 虚函数的使用 以及虚函数与重载的关系, 空虚函数的作用,纯虚函数->抽象类,基类虚析构函数使释放对象更彻底

    为了访问公有派生类的特定成员,可以通过讲基类指针显示转换为派生类指针. 也可以将基类的非静态成员函数定义为虚函数(在函数前加上virtual) #include<iostream> usi ...

  5. C++ 系列:虚函数

    Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...

  6. EC笔记,第二部分:9.不在构造、析构函数中调用虚函数

    9.不在构造.析构函数中调用虚函数 1.在构造函数和析构函数中调用虚函数会产生什么结果呢? #; } 上述程序会产生什么样的输出呢? 你一定会以为会输出: cls2 make cls2 delete ...

  7. C++构造函数中不能调用虚函数

    在构造函数中调用虚函数,并不会产生多态的效果,就跟普通函数一样. c++ primer 第四版中497页15.4.5构造函数和析构中的虚函数讲到,如果在构造函数或析构函数中调用虚函数,则运行的是为构造 ...

  8. C#虚函数和接口的区别

    接口只能声明不能实现,虚函数可以. 接口:对外提供可以访问的函数叫接口.虚函数不需要被强制重写,其本身含有实现部分. 抽象类:指派了派生类必须实现的函数(纯虚函数),不然编译不通过. 虚函数的限制:  ...

  9. c++ 虚函数

    class A { public: virtual void f();//希望派生类重写 void fun();//绝大多数情况下不要重新定义基类的非虚函数,那样会打破公有继承Is-A的关系,而且行为 ...

随机推荐

  1. UNIX环境高级编程——环境变量表读取/添加/修改/删除

    #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char* pVal ...

  2. 【java集合框架源码剖析系列】java源码剖析之HashSet

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于HashSet的知识. 一HashSet的定义: public class HashSet&l ...

  3. [java面试]逻辑推理6 10 18 32 下一个数?编程实现输入任意一个N位置,该数是多少?java实现

    题目: 6 10 18 32 下一个数?编程实现输入任意一个N位置,该数是多少? 10 = 6 + 4         4 18 = 10 + 8        4 + 4  32 = 18 + 14 ...

  4. Cocos2D iOS之旅:如何写一个敲地鼠游戏(六):放置地鼠

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  5. 05_NoSQL数据库之Redis数据库:Redis的常用命令,键值相关命令和服务器相关命令

     Redis常用命令 Redis提供了丰富的命令对数据库和各种数据库类型进行操作,这些命令可以再Linux终端使用. 键值相关命令: Keys:返回满足给定pattern的所有key 用表达式*表 ...

  6. Swift中实现Observable机制

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/51917539 ...

  7. iOS中 陀螺仪/加速器 韩俊强的博客

    引进框架: #import <CoreMotion/CoreMotion.h> 定义属性初始化相关: #import "ViewController.h" #impor ...

  8. Unity2D Sprite灰白图(Unity3D开发之十一)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2D开发网–Cocos2Dev.com,谢谢! 原文地址: http://www.cocos2dev.com/?p=596 昨晚看到群里问到2DSpr ...

  9. HTML5 预加载

    原文地址: HTML5 Link Prefetching 原文日期: 2010年07月07日 翻译日期: 2013年08月13日 浏览器厂商和开发者之间共同努力的一个方向就是让网站更快.现在已有很多广 ...

  10. 我眼中的Linux设备树(六 memory&chosen节点)

    六 memory&chosen节点根节点那一节我们说过,最简单的设备树也必须包含cpus节点和memory节点.memory节点用来描述硬件内存布局的.如果有多块内存,既可以通过多个memor ...