C++中析构函数能够为纯虚函数吗?

众所周知。在实现多态的过程中,一般将基类的析构函数设为virtual。以便在delete的时候能够多态的链式调用。那么析构函数能否够设为纯虚呢?

class CBase
{
public:
CBase()
{
printf("CBase()\n");
}
virtual ~CBase() = 0; // 析构函数是纯虚函数
};

答案是能够。那么这样实现的目的是什么呢?当然是避免实例化

但由于派生类不可能来实现基类的析构函数,所以基类析构函数尽管能够标为纯虚,可是仍必须实现析构函数,否则派生类无法继承。也无法编译通过

以下详细讨论:

虚析构函数

我们知道,为了能够正确的调用对象的析构函数,一般要求具有层次结构的顶级类定义其析构函数为虚函数。由于在delete一个抽象类指针时候。必须要通过虚函数找到真正的析构函数。

如:

class Base
{
public:
Base(){}
virtual ~Base(){}
}; class Derived: public Base
{
public:
Derived(){};
~Derived(){};
} void foo()
{
Base *pb;
pb = new Derived;
delete pb;
}

这是正确的使用方法,会发生动态绑定。它会先调用Derived的析构函数,然后是Base的析构函数

假设析构函数不加virtualdelete pb仅仅会运行Base的析构函数,而不是真正的Derived析构函数。

由于不是virtual函数,所以调用的函数依赖于指向静态类型,即Base

纯虚析构函数

如今的问题是。我们想把Base做出抽象类,不能直接构造对象,须要在当中定义一个纯虚函数。

假设当中没有其它合适的函数,能够把析构函数定义为纯虚的。即将前面的CObject定义改成:

class Base
{
public:
Base(){}
virtual ~Base()= 0;
};

可是。这段代码不能通过编译。一般是link错误。不能找到~Base()的引用(gcc的错误报告)。

error LNK2019: unresolved external symbol "public: virtual __thiscall Base::~Base(void)" (??1Base@@UAE@XZ) referenced in function "public: virtual __thiscall Derived::~Derived(void)" (??

1Derived@@UAE@XZ)
1>D:\C++\exercise\Debug\exercise.exe : fatal error LNK1120: 1 unresolved externals

这是由于。析构函数、构造函数和其它内部函数不一样。在调用时,编译器须要产生一个调用链

也就是,Derived的析构函数里面隐含调用了Base的析构函数。而刚才的代码中,缺少~Base()的函数体。当然会出现错误。

这里面有一个误区,有人觉得,virtual f()=0这样的纯虚函数语法就是未定义体的语义。

事实上,这是不正确的。这样的语法仅仅是表明这个函数是一个纯虚函数因此这个类变成了抽象类,不能产生对象

我们全然能够为纯虚函数指定函数体 。通常的纯虚函数不须要函数体,是由于我们一般不会调用抽象类的这个函数,仅仅会调用派生类的相应函数。

这样。我们就有了一个纯虚析构函数的函数体,上面的代码须要改成:

class Base
{
public:
Base(){}
virtual ~Base() = 0; //pure virtual
};
Base::~Base()//function body
{
}

从语法角度来说,不能够将上面的析构函数直接写入类声明中(内联函数的写法)。

这也许是一个不正交化的地方。

可是这样做的确显得有点累赘

这个问题看起来有些学术化,由于一般我们全然能够在Base中找到一个更加适合的函数,通过将其定义为没有实现体的纯虚函数,而将整个类定义为抽象类。但这样的技术也有一些应用,如这个样例:

class Base  //abstract class
{
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
{
Base::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虚函数定义必须和父类全然一致(c++11中使用override进行编译器检查)。除了一个特例,假设父类中返回值是一个指针或引用。子类override时能够返回这个指针(或引用)的派生。

    比如,在上面的样例中,在Base中定义了 virtual Base* clone(); 在Derived中能够定义为 virtual Derived* clone()

    能够看到,这样的放松对于Clone模式是非常实用的(也就是说override并不会检查返回值类型)。

C++中的虚析构函数、纯虚析构函数具体解释的更多相关文章

  1. 虚函数&纯虚函数&抽象类&虚继承

    C++ 虚函数&纯虚函数&抽象类&接口&虚基类   1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过 ...

  2. C++中的抽象类及纯虚函数的实现与否

    1.含有纯虚函数的叫抽象类 2.抽象类(一般是基类)中的纯虚函数无论函数体实现与否,都没有关系,系统会自动忽略 3.继承自抽象类的子类,必须要实现父类的纯虚函数才可以实例化对象 4.抽象类不允许实例化 ...

  3. 【转】C++ 虚函数&纯虚函数&抽象类&接口&虚基类

    1. 动态多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过父类的指针调用实际子类的成员函数. 多态性就是允许将子类类型的指针赋值给父类类型 ...

  4. C++ 虚函数&纯虚函数&抽象类&接口&虚基类(转)

    http://www.cnblogs.com/fly1988happy/archive/2012/09/25/2701237.html 1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多 ...

  5. C++中纯虚函数

    1.纯虚函数 virtual ReturnType Function()= 0; 纯虚函数可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义.凡是含有纯虚函数的类叫做抽象类 ...

  6. C++学习基础十二——纯虚函数与抽象类

    一.C++中纯虚函数与抽象类: 1.含有一个或多个纯虚函数的类成为抽象类,注意此处是纯虚函数,而不是虚函数. 2.如果一个子类继承抽象类,则必须实现父类中的纯虚函数,否则该类也为抽象类. 3.如果一个 ...

  7. C++学习笔记(十二):类继承、虚函数、纯虚函数、抽象类和嵌套类

    类继承 在C++类继承中,一个派生类可以从一个基类派生,也可以从多个基类派生. 从一个基类派生的继承称为单继承:从多个基类派生的继承称为多继承. //单继承的定义 class B:public A { ...

  8. C++多态、虚函数、纯虚函数、抽象类、虚基类

    一.C++多态 C++的多态包括静态多态和动态多态.静态多态包括函数重载和泛型编程,动态多态包括虚函数.静态多态是指在编译期间就可以确定,动态多态是指在程序运行时才能确定. 二.虚函数 1.虚函数为类 ...

  9. C++基础知识 基类指针、虚函数、多态性、纯虚函数、虚析构

    一.基类指针.派生类指针 父类指针可以new一个子类对象 二.虚函数 有没有一个解决方法,使我们只定义一个对象指针,就可以调用父类,以及各个子类的同名函数? 有解决方案,这个对象指针必须是一个父类类型 ...

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

    首先,面向对象程序设计(object-oriented programming)的核心思想是数据抽象.继承.动态绑定.通过数据抽象,可以使类的接口与实现分离,使用继承,可以更容易地定义与其他类相似但不 ...

随机推荐

  1. android图片特效处理之光晕效果

    这篇将讲到图片特效处理的图片光晕效果.跟前面一样是对像素点进行处理,本篇实现的思路可参见android图像处理系列之九--图片特效处理之二-模糊效果和android图像处理系列之十三--图片特效处理之 ...

  2. 建堆是 O(n) 的时间复杂度证明。

    建堆的复杂度先考虑满二叉树,和计算完全二叉树的建堆复杂度一样. 对满二叉树而言,第 \(i\) 层(根为第 \(0\) 层)有 \(2^i\) 个节点. 由于建堆过程自底向上,以交换作为主要操作,因此 ...

  3. Sql延时

    IF EXISTS(SELECT * FROM sys.procedures WHERE name='usp_wait30s')BEGIN DROP PROC usp_wait30sENDgocrea ...

  4. 洛谷P1043 数字游戏

    题目描述 丁丁最近沉迷于一个数字游戏之中.这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易.游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分 ...

  5. 通过Ajax进行POST提交JSON类型的数据到SpringMVC Controller的方法

    现在在做的项目用到了SpringMVC框架,需要从前端angular接收请求的JSON数据,为了测试方便,所以直接先用AJAX进行测试,不过刚开始用平时用的ajax方法,提交请求会出现415或者400 ...

  6. 用实力让情怀落地!阅兵前线指挥车同款电视TCL&#160;H8800受捧

        近期.一则重磅消息刷爆了平面媒体.微博.朋友圈等各个传播渠道:TCL曲面电视H8800正式入驻大阅兵前线指挥车以及国旗护卫队荣誉室.宣告代表眼下中国彩电业最高技术水准的曲面电视,正式走上大阅兵 ...

  7. Android中使用HttpClient实现HTTP通信效果

    HTTP通信,这一案例在操作的时候遇到N多种种问题,是前面看过几个实例里面最麻烦的一个.由于没有系统的接触过JAVA,所以出了非常多错误,也无从下手解决,这里经过对错误的检索实现了HTTP通信,以做记 ...

  8. 【JavaScript】--JavaScript总结一览无余

    对于 北风网李炎恢老师的JavaScript的视频也真的是醉了.视频整体来说结构清晰.内容比較简单.JS是一种灵活,开放的语言,语法规则并没有那么的死板.非常easy让人接受. JS的基础部分跟C#类 ...

  9. 一个简单http请求的jmeter压测实战流程

    1.新建线程组 2.创建http请求 注意:接口路径中的参数值要写变量 3.创建txt文件,存多个参数值 4.创建csv文件,在csv中上传txt文件 5.variable name填写txt中参数值 ...

  10. 【习题 6-5 UVA-1600】Patrol Robot

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 设dis[x][y][z]表示到(x,y)连续走了z个墙的最短路 bfs一下就ok [代码] /* 1.Shoud it use l ...