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

C++ Primer学习笔记(14)——虚函数的实现机制、纯虚函数

2015-07-08 20:46 501 查看
在行文之前查阅了相关书籍,参考了一些别人的博客,在这里谢谢大家的分享!希望自己和大家在学习语言的道路上渐行渐远,一直走下去~~~

上一篇文章中说道,C++ 的三个基本特质是 封装、继承、多态。

多态性是将接口与实现进行分离。用形象的语言解释就是实现已共同的方法,但因个体差异而采用不同的策略。

多态包括静多态和动多态,分别在编译和运行过程中实现。而动多态是由虚函数来实现的,其实现机制体现了C++的神秘性。

1.虚函数的实现机制

虚函数是那些以 virtual 关键字修饰的成员函数,是用来实现多态的。

两个重要的关键词揭示了它的神秘面纱 : 虚表、虚指针。每个类用了一个虚表,每个类对象用了一个虚指针。

具体演示如下:

class A
{
public:
virtual void f();
virtual void g();
private:
int a;
};

class B : public A
{
public:
void g();   // 覆盖父类A中的虚函数g()
private:
int b;
};


因为 A 有 virtual void f( ) ,和 g( ),所以编译器为 A 类准备了一个虚表 vtableA,内容如下:



B 继承了A,所以里面也有继承于A的虚函数,故B也有一个虚表vtableB:



PS:因为B:: g是重写了的,所以B的虚表的g放的是B::g的入口地址,而f 还是A:: g 的入口地址。

当执行B bt; 定义类对象的时候,编译器分配空间时,除了 A 的int a 成员 , B的 int b 成员,还分配了指向B的虚表vtableB 的指针vptr , 对象bt 的布局如下:



其中,虚表指针总是在最前面的。

当执行下面的语句时:

A *pa = &bt;  //一个A 类型的指针,指向B类型的对象bt


pa ->g( ) 实际上是由 指针vptr指向B的虚表vtableB,然后去里面寻找g()函数。可以看到,vtableB里面是 B:: g( ),即B 自己的g( ),而不是A 的g( )。

这就是多态。

总结:

要知道 虚函数实现多态的机制,谨记虚表、虚指针。能够列出父类和子类的虚表,以及子类对象的虚指针,就不难理解这个机制了。

2. 纯虚函数(pure virtual function)

很多时候,定义一个类的对象是没有意义的,例如,动物这个类,由它可以派生出大象、狮子、猴子等子类,但动物本身生成对象是没有实际意义的。为了解决这个问题,提出了纯虚函数的概念。

假如定义的虚函数为:

virtual void animals(string& name, int age){}


那么如下就是纯虚函数:

virtual void animals(string& name, int age) = 0;   // 函数体直接为0 的虚函数


存在纯虚函数的类称为 抽象类,是一个单纯地接口,抽象类本身实例化,即不能生成对象,只能也必须在派生类中去实例化。

什么情况下使用纯虚函数呢?

当想在基类中抽象出一种方法,且该基类只能被继承,不能实例化

这个方法必须在派生类中实现

针对上述两点分别举例说明:

1) 抽象类不能实例化

例如:定义一个形状的类(Cshape),但凡是形状我们都要求能显示自己,所以定义一个类如下:

class Cshape
{
virtual show(){}
};


我们不想将这个类实例化,首先会想到将show( )这个函数的函数体 { } 删除,改为 virtual show( );

这时如果尝试实例化 Cshape shape; 这样其实是能够通过编译的,只能在连接的时候报错。

那么如何能够在编译的时候就报错呢?

~~用纯虚函数:

class Cshape
{
virtual show() = 0; // 纯虚函数不能实例化
};


2) 该方法必须在派生类中实现

如上面的例子,show( ) 必须在每一个子类中实现,即在子类中重新定义自己的 show( ) 函数,若没有定义,编译时就会报错:

// 定义Cshape 的派生类,忘了定义show()
class CPoint : public Cshape
{
public:
void msg()
{
cout << " 这是一个点。" << endl;
}
private:
float x;
float y;
};

// 下面实例化 CPoint 类的时候就会报错!!!
CPoint point1;


在编译的时候就会报错!!!

~~~~

这是不是一个预防在派生类中实现基类的方法? (^__^)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: