您的位置:首页 > 编程语言 > C语言/C++

VC++入门经典学习笔记--纯虚函数

2016-09-14 15:56 162 查看

1.纯虚函数

我们可能希望在基类中包括一个虚函数,这样就可以在派生类中为适应派生类对象而重新定义该函数,但在基类中无法给予该函数任何有意义的定义。

新建一个CContainter类,它可以用作定义CBox类的基类。CContainter类将没有数据成员,但我们可能希望为任何派生类提供一个允许被多态调用的虚成员函数volume()。因为CContainer类没有任何数据成员,因此不占用磁盘空间,所以不能为volume()函数编写有意义的定义。然而,我们仍然能够定义这个类–当然包括成员函数volume()

CContainer类定义的代码如下所示:

#pragma once
#include <iostream>

class CContainter
{
public:
//纯虚函数
virtual double volume() const = 0;

virtual void showVolume() const
{
std::cout << "Volume is" << volume() << std::endl;
}
};


定义虚函数volume()的语句在函数头中添加等号0,将该函数定义成没有任何内容。这样的函数称为春旭函数。该类还包含一个现实派生类对象体积的函数showVolume()。因为该函数声明为virtual,所以在派生类中可以替换。但是如果不进行替换,则程序将调用这里的基类版本。

2.抽象类

包含纯虚函数的类称为抽象类,因为不能定义包含纯虚函数的类的对象。但可以定义抽象类的指针和引用。抽象类存在的唯一用途,就是定义派生类。如果抽象类的派生类将基类的纯虚函数仍然定义为纯虚函数,则该派生类也是抽象类。

我们不应该从上一个CCOntainer类的示例中得出抽象类不能拥有数据成员的结论。抽象类可以拥有数据成员和函数成员。纯虚函数是否存在时判断给定的类是否是抽象类的唯一条件。同样的道理,抽象类可以拥有多个纯虚函数。这种情况下,派生类必须给出基类中每个纯虚函数的定义,否则将仍然是抽象类。如果忘记将派生类的volume()函数指定为const,则派生类同样将仍然是抽象类,因为它不仅包含定义的非const函数volume(),还包含const纯虚函数成员volume()。const函数和非const函数总是不同的。

CBox.h继承抽象类:

#pragma once
#include <iostream>
#include "CContainter.h"

using std::cout;
using std::endl;
class CBox:public CContainter
{
public:
//输出箱子体积函数
void showVolume() const{ cout << "CBox的体积是" << volume() << endl; }

//虚函数
virtual double volume()const{ return m_Length*m_Width*m_Height; }

//构造函数(在初始化列表中设定数据成员的值,因此其函数体中不需要任何语句)
CBox(double lv = 1.0, double wv = 1.0, double hv = 1.0) :m_Length{ lv }, m_Width{ wv }, m_Height{ hv }{}

//声明箱子的成员变量(数据成员指定为protected,因此任何派生类的成员函数都可以访问它们)
protected:
double m_Length;
double m_Width;
double m_Height;
};


CBox类实质上和以前的示例中一样 ,只是这次将其指定为从CContainer类派生。volume()函数完全在这个类中定义(如果CBox类要用来定义对象,那么必须如此)。仅有的其他选择是将其指定为纯虚函数,因为该函数字基类中就是纯虚函数,但那样将不能创建CBox对象。

CCan.h继承抽象类:

#pragma once
#define _USE_MATH_DEFINES
#include <math.h>
#include "CContainter.h"

class CCan : public CContainter
{
public:
virtual double volume() const
{
return 0.25*M_PI*m_Diameter*m_Height;
}
//构造函数
CCan(double hv = 4.0, double dv = 2.0) :m_Height{ hv }, m_Diameter{ dv }{}
protected:
double m_Height;
double m_Diameter;
};


CCan类也定义了volume()函数,只不过依据的公式是hπr2,这里的.h是罐的高度,r是罐体横截面的半径。M_PI常量在math.h中定义,在定义_USE_MATH_DEFINES后,它就是可用的。math.h头文件定义了许多其他数学常量,如果把光标放在代码的math.h上,按下Ctrl+Shift+G就可以看到这些常量。

注意,在CBox类中重新定义了showVolume()函数,但在CCan函数中没有这样做。当得到程序的输出时,将看到结果有所不同。

输出结果:

CBox的体积是24
Volume is15.3153


在这个程序中,声明了两个指向基类CContainer的指针。虽然不能定义CContaier对象(因为CContainer是抽象类),但仍然可以定义指向CContainer*类型的指针,然后可以使用该指针来存储直接或间接派生自CContainer的类对象的地址。指针pC1被赋予在空闲存储器中由new操作符创建的CBox对象的地址。第二个指针以类似的方式被赋予CCan对象的地址。当然,由于派生类对象是动态创建的,因此不再需要他们时必须使用delete操作符清理空闲存储器。

因为已经在CBox类中定义了showVolume()函数,所以CBox对象将调用该函数的派生类版本。在CCan类中没有定义这个函数,因此CCan对象将调用从基类继承的街垒版本。因为volume()函数在两个派生类中都是以虚函数形式实现的(必须如此,因为该函数在基类中时纯虚函数),所以在程序执行时解析该函数的调用,选中的函数版本将属于被指向的对象所属的类。因此,对指针pC1来说,被调用的是CBox类的函数版本。对指针pC2而言,被调用的确实CCan类的函数版本。因此在每种情况下,我们都获得正确的结果。

还可以只使用一个指针,在调用CBox对象的volume()函数之后,赋予该指针对象CCan的地址。基类指针可以包含任何派生类对象的地址,即使相同的基类派生出多个不同的子类也无妨。因此,可以在整个派生类的范围内自动选择适当的虚函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息