虚析构函数(√)、纯虚析构函数(√)、虚构造函数(X)
from:http://blog.csdn.net/fisher_jiang/article/details/2477577
一. 虚析构函数
我们知道,为了能够正确的调用对象的析构函数,一般要求具有层次结构的顶级类定义其析构函数为虚函数。因为在delete一个抽象类指针时候,必须要通过虚函数找到真正的析构函数。
如:
{
public:
Base(){}
virtual ~Base(){}
};
class Derived: public Base
{
public:
Derived(){};
~Derived(){};
}
void foo()
{
Base *pb;
pb = new Derived;
delete pb;
}
这是正确的用法,会发生动态绑定,它会先调用Derived的析构函数,然后是Base的析构函数
如果析构函数不加virtual,delete pb只会执行Base的析构函数,而不是真正的Derived析构函数。
因为不是virtual函数,所以调用的函数依赖于指向静态类型,即Base
二. 纯虚析构函数
现在的问题是,我们想把Base做出抽象类,不能直接构造对象,需要在其中定义一个纯虚函数。如果其中没有其他合适的函数,可以把析构函数定义为纯虚的,即将前面的CObject定义改成:
{
public:
Base(){}
virtual ~Base()= 0
};
这里面有一个误区,有人认为,virtual f()=0这种纯虚函数语法就是没有定义体的语义。
其实,这是不对的。这种语法只是表明这个函数是一个纯虚函数,因此这个类变成了抽象类,不能产生对象。我们完全可以为纯虚函数指定函数体 。通常的纯虚函数不需要函数体,是因为我们一般不会调用抽象类的这个函数,只会调用派生类的对应函数。这样,我们就有了一个纯虚析构函数的函数体,上面的代码需要改成:
{
public:
Base()
{
}
virtual ~Base() = 0; //pure virtual
};
Base::~Base()//function body
{
}
从语法角度来说,不可以将上面的析构函数直接写入类声明中(内联函数的写法)。这或许是一个不正交化的地方。但是这样做的确显得有点累赘
这个问题看起来有些学术化,因为一般我们完全可以在Base中找到一个更加适合的函数,通过将其定义为没有实现体的纯虚函数,而将整个类定义为抽象类。但这种技术也有一些应用,如这个例子:
{
public:
virtual ~Base(){};//virtual, not pure
virtual void Hiberarchy() const = 0;//pure virtual
};
void Base::Hiberarchy() const //pure virtual also can have function body
{
std::cout <<"Base::Hiberarchy";
}
class Derived : public Base
{
public:
Derived(){}
virtual void Hiberarchy() const
{
CB::Hiberarchy();
std::cout <<"Derived::Hiberarchy";
}
virtual void foo(){}
};
int main(){
Base* pb=new Derived();
pb->Hiberarchy();
pb->Base::Hiberarchy();
return 0;
}
在这个例子中,我们试图打印出类的继承关系。在根基类中定义了虚函数Hiberarchy,然后在每个派生类中都重载此函数。我们再一次看到,由于想把Base做成个抽象类,而这个类中没有其他合适的方法成员可以定义为纯虚的,我们还是只好将Hiberarchy定义为纯虚的。(当然,完全可以定义~Base函数,这就和上面的讨论一样了。^_^)
另外,可以看到,在main中有两种调用方法,第一种是普通的方式,进行动态链接,执行虚函数,得到结果"Derived::Hiberarchy";第二种是指定类的方式,就不再执行虚函数的动态链接过程了,结果是"Base::Hiberarchy"。
通过上面的分析可以看出,定义纯虚函数的真正目的是为了定义抽象类,而并不是函数本身。与之对比,在java中,定义抽象类的语法是 abstract class,也就是在类的一级作指定(当然虚函数还是也要加上abstract关键字)。是不是这种方式更好一些呢?在Stroustrup的《C++语言的设计与演化》中我找到这样一段话:
“我选择的是将个别的函数描述为纯虚的方式,没有采用将完整的类声明定义为抽象的形式,这是因为纯虚函数的概念更加灵活一些。我很看重能够分阶段定义类的能力;也就是说,我发现预先定义一些纯虚函数,并把另外一些留给进一步的派生类去定义也是很有用的”。
我还没有完全理解后一句话,我想从另外一个角度来阐述这个概念。那就是,在一个多层复杂类结构中,中间层次的类应该具体化一些抽象函数,但很可能并不是所有的。中间类没必要知道是否具体化了所有的虚函数,以及其祖先已经具体化了哪些函数,只要关注自己的职责就可以了。也就是说,中间类没必要知道自己是否是一个真正的抽象类,设计者也就不用考虑是否需要在这个中间类的类级别上加上类似abstract的说明了。
当然,一个语言的设计有多种因素,好坏都是各个方面的。这只是一个解释而已。
最后,总结一下关于虚函数的一些常见问题:
1) 虚函数是动态绑定的,也就是说,使用虚函数的指针和引用能够正确找到实际类的对应函数,而不是执行定义类的函数。这是虚函数的基本功能,就不再解释了。
2) 构造函数不能是虚函数。而且,在构造函数中调用虚函数,实际执行的是父类的对应函数,因为自己还没有构造好, 多态是被disable的。
3) 析构函数可以是虚函数,而且,在一个复杂类结构中,这往往是必须的。
4) 将一个函数定义为纯虚函数,实际上是将这个类定义为抽象类,不能实例化对象。
5) 纯虚函数通常没有定义体,但也完全可以拥有。
6) 析构函数可以是纯虚的,但纯虚析构函数必须有定义体,因为析构函数的调用是在子类中隐含的。
7) 非纯的虚函数必须有定义体,不然是一个错误。
8) 派生类的override虚函数定义必须和父类完全一致。除了一个特例,如果例如,在上面的例子中,在Base中定义了 virtual Base* clone(); 在Derived中可以定义为 virtual Derived* clone()。可以看到,这种放松对于Clone模式是非常有用的。
其他,有待补充。
虚析构函数(√)、纯虚析构函数(√)、虚构造函数(X)的更多相关文章
- 从零开始学C++之虚函数与多态(二):纯虚函数、抽象类、虚析构函数
一.纯虚函数 虚函数是实现多态性的前提 需要在基类中定义共同的接口 接口要定义为虚函数 如果基类的接口没办法实现怎么办? 如形状类Shape 解决方法 将这些接口定义为纯虚函数 在基类中不能给出有意义 ...
- 构造函数为什么不能为虚函数 & 基类的析构函数为什么要为虚函数
一.构造函数为什么不能为虚函数 1. 从存储空间角度,虚函数相应一个指向vtable虚函数表的指针,这大家都知道,但是这个指向vtable的指针事实上是存储在对象的内存空间的.问题出来了,假设构造函数 ...
- 从零开始学C++之虚函数与多态(一):虚函数表指针、虚析构函数、object slicing与虚函数
一.多态 多态性是面向对象程序设计的重要特征之一. 多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为. 多态的实现: 函数重载 运算符重载 模板 虚函数 (1).静态绑定与动态绑 ...
- 【转】C++析构函数为什么要为虚函数
注:本文内容来源于zhice163博文,感谢作者的整理. 1.为什么基类的析构函数是虚函数? 在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生. 下面转自网络:源地址 ...
- C++析构函数为什么要为虚函数
注:本文内容来源于zhice163博文,感谢作者的整理. .为什么基类的析构函数是虚函数? 在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生. 下面转自网络:源地址 h ...
- 基类的析构函数写成virtual虚析构函数
虚函数作用:动态绑定,实现多态效果. 场景问题: 派生类中有资源需要回收,而在编程中采用多态,由基类的指针指向派生类,则在释放的时候,如果基类的析构函数不是virtual,则派生类的析构函数得不到释放 ...
- C++多态、虚函数、纯虚函数、抽象类、虚基类
一.C++多态 C++的多态包括静态多态和动态多态.静态多态包括函数重载和泛型编程,动态多态包括虚函数.静态多态是指在编译期间就可以确定,动态多态是指在程序运行时才能确定. 二.虚函数 1.虚函数为类 ...
- 虚函数&纯虚函数&抽象类&虚继承
C++ 虚函数&纯虚函数&抽象类&接口&虚基类 1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过 ...
- 【转】C++ 虚函数&纯虚函数&抽象类&接口&虚基类
1. 动态多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过父类的指针调用实际子类的成员函数. 多态性就是允许将子类类型的指针赋值给父类类型 ...
- C++ 虚函数&纯虚函数&抽象类&接口&虚基类(转)
http://www.cnblogs.com/fly1988happy/archive/2012/09/25/2701237.html 1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多 ...
随机推荐
- thinkphp 3.2 单入口 多模块 不能加载index控制器问题
菜鸟一个,大神不用看,很喜欢单入口 多模块的方式,所以想自己设置下,结果看很多教程没看懂,也看到有人在问这个问题,分享下我的项目名称是app,首先运行官方的index.php文件,app目录下生成了三 ...
- ubuntu14.04配置中文latex完美环境(texlive+texmaker+lyx)
Ubuntu下的文档编辑虽然有libreoffice,但对中文和公式的排版始终不如ms office,因此要想写出高质量的文档,只能靠latex了,现在随着xeCjk的开发,中文文档在ubuntu下的 ...
- 神器Alfred使用
神器官网:http://www.alfredapp.com/ 许多人装好了之后就再也没有怎么使用过,这软神器, 一直不明白它为什么叫神器,最近研究了一下,真的是,很多东西我们没有好好利用 部分使用功能 ...
- ubuntu显示桌面的快捷键,以及修改方法
在ubuntu下面,快速显示桌面,你可以这样做. 1,ctrl+alt+d (默认的) 2,alt+tab 可以切换到桌面 但是我想把它修改成和windows一样的,我该怎么做呢? 其实很简单. 系统 ...
- mysql链接数据库时报错
今天在命令行下链接mysql数据库报错,如下: ERROR (HY000): Can't connect to MySQL server on 'ost' (113) 这是一个什么玩意呢,怎么会报这个 ...
- 【原创】angularjs1.3.0源码解析之directive
# Angular指令编译原理 前言 angular之所以使用起来很方便,是因为通常我们只需要在html里面引入一个或多个(自定义或内置的)指令就可以完成一个特定的功能(这也是angular推荐的方式 ...
- 跟着百度学PHP[1]-if条件嵌套
权当自己的学习笔记.望大牛们切勿参考.如若发现错误,万望指出! 慕课任务 假设在发工资的时候,不仅判定性别,还要判定男性是否有房,没有房,可以发放住房补贴,对于女性,判定是否怀孕,怀孕还有怀孕补贴. ...
- VS2010中如何查看DLL的导出接口
看<VC++动态链接库(DLL)编程深入浅出>时,里面提到使用Visual C++的Depends工具可以查看动态链接库中的导出接口.对于VC6.0,VC所带的Depends软件,在VC6 ...
- javascript对象转化为基本数据类型规则
原文:Object-to-Primitive Conversions in JavaScript 对象转化为基础数据类型,其实最终都是用调用对象自带的valueOf和toString两个方法之一并获得 ...
- C 结构体小结
看了三天结构体,是时候总结一下了. 关于结构体的声明: struct Student { ]; char sex; int age; ]; }; /*然后定义一个Student 类型的 student ...