C++语言特性之“虚”

host
host
host
33
文章
0
评论
2017年3月12日09:58:06C++语言特性之“虚”已关闭评论285

虚析构函数

虚析构函数是为了解决基类的指针指向派生类对象,并用基类的指针删除派生类对象

虚析构函数的使用

class Shape
{
public:
    Shape();                    // 构造函数不能是虚函数
    virtual double calcArea();
    virtual ~Shape();           // 虚析构函数
};
class Circle : public Shape     // 圆形类
{
public:
    virtual double calcArea();
    ...
};
int main()
{
    Shape * shape1 = new Circle(4.0);
    shape1->calcArea();    
    delete shape1;  // 因为Shape有虚析构函数,所以delete释放内存时,先调用子类析构函数,再调用基类析构函数,防止内存泄漏。
    shape1 = NULL;
    return 0;
}

纯虚函数

纯虚函数是一种特殊的虚函数,在基类中不能对虚函数给出有意义的实现,而把它生命为纯虚函数,他的实现留给基类的派生类去做。

virtual int A() = 0;

虚函数、纯虚函数

  1. 类里面如果声明了虚函数,这个函数是实现的,哪怕是空实现,它的作用就是为了能让这个函数在它的子类里面可以被覆盖(override),这样的话,编译器就可以使用后期绑定来达到多态了。纯虚函数只是一个接口,是个函数的声明而已,它要留到子类里面去实现。
  2. 虚函数在子类里可以不重写;但纯虚函数必须在子类实现才可以实例化子类。
  3. 虚函数的类用于“实作继承”,继承接口的同时也继承了父类的实现。纯虚函数关注的是接口的统一性,实现由子类完成。
  4. 带纯虚函数的类叫抽象类,这种类不能直接生成对象,而只有被继承,被重写其虚函数后,才能使用。这样类也叫虚基类。抽象类和大家口头常说的虚基类还是有区别的,在C#中abstract定义抽象类,而在C++中有抽象类的概念,但没有这个关键字。抽象类被继承后,子类可以继续是抽象类,也可以是普通类。而虚基类,是含有纯虚函数的类,它如果被继承,那么子类就必须实现虚基类里面的所有纯虚函数,其子类不能是抽象类。
  5. 有时,声明一个除纯虚函数外什么也不包含的类很有用。这样的类叫协议类(Protocol class),它为派生类仅提供函数接口,完全没有实现。
  6. C++中没有接口的概念,与之对应的是纯虚类,即只含有纯虚函数的类,c++抽象类的概念是含有纯虚函数成员的类。这是因为C++提供多继承,而像 Java、C#这些只提供单继承(避免多继承的复杂性和低效性)的语言为了模拟多继承功能就提供了接口概念,接口可以继承多个。
  7. 在Java里面的确没有纯虚类的概念,因为Java里没有纯虚函数这个概念,Java管虚函数叫abstract function,管抽象类叫做abstract class,直接来说,Java根本没有virtual这个关键字,都是用abstract代替,因此Java里面根本没有Pure这个概念。有那就是interface。在interface里面定义的函数都不能有函数体,这个在Java里面叫接口。那么C++里面与interface等同的概念就是纯虚类了,C++用纯虚类来模拟interface这个抽象概念,因此这里说的“纯虚类”与Java的abstract class不同,与C++的一般抽象类也不同。“纯虚类”与C++一般抽象类的区别就好比Java里面interface和abstract class的区别。

虚函数指针、虚函数表

  • 虚函数指针:在含有虚函数类的对象中,指向虚函数表,在运行时确定。
  • 虚函数表:在程序只读数据段(.rodata section,见:目标文件存储结构),存放虚函数指针,如果派生类实现了基类的某个虚函数,则在虚函数表中覆盖原本基类的那个虚函数表指针,在编译时根据类的声明创建。

C++中的虚函数(表)实现机制以及用C语言对其进行的模拟实现

虚继承

虚继承用于解决多继承条件下的菱形继承问题(浪费存储空间、存在二义性)。

底层实现原理与编译器相关,一般通过虚基类指针虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面进行拷贝,只是仅仅多存一份而已,并不是不在子类里面了);当虚继承的子类被当作父类继承时,虚基类指针也会被继承。

实际上,vbptr指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。

虚继承、虚函数

  • 相同之处:都利用了虚指针(均占用类的存储空间)和虚表(均不占用类的存储空间)
  • 不同之处:
    • 虚继承
      • 虚基类依旧存在继承类中,只占用存储空间
      • 虚基类表存储的是虚基类相对直接继承类的偏移
    • 虚函数
      • 虚函数不占用存储空间
      • 虚函数表存储的是虚函数地址
host
  • 本文由 发表于 2017年3月12日09:58:06
  • 转载请务必保留本文链接:https://www.zenook.cn/language/c-c/cvirtual.html
面向对象特性之多态 语言

面向对象特性之多态

多态 多态,即多种状态(形态)。简单来说,我们可以将多态定义为消息以多种形式显示的能力。 多态是以封装和继承为基础的 C++多态分类及实现 重载多态(Ad-hoc Polymorphism,编译期):...
C/C++关键字之inline C/C++

C/C++关键字之inline

特征 相当于把内联函数里面的内容写在调用内联函数处 相当于不用执行进入函数的步骤,直接执行函数体 相当于宏,却比宏多了类型检查,真正具有函数特性 编译器一般不内联包含循环、递归、switch等复杂操作...
C/C++关键字之inline C/C++

C/C++关键字之inline

特征 相当于把内联函数里面的内容写在调用内联函数处 相当于不用执行进入函数的步骤,直接执行函数体 相当于宏,却比宏多了类型检查,真正具有函数特性 编译器一般不内联包含循环、递归、switch等复杂操作...
C/C++内存分配和管理 C/C++

C/C++内存分配和管理

malloc、calloc、realloc、alloca malloc:申请指定字节数的内存。申请到的内存中的初始值不确定 calloc:为指定长度的对象,分配能容纳其指定个数的内存。申请到的内存的每...