关闭

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

标签: 虚函数纯虚函数多态C++
444人阅读 评论(0) 收藏 举报
分类:

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

上一篇文章中说道,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;

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

~~~~
这是不是一个预防在派生类中实现基类的方法? (^__^)

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:4327次
    • 积分:180
    • 等级:
    • 排名:千里之外
    • 原创:14篇
    • 转载:2篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档