虚函数&虚继承

之前不了解虚继承,一度以为是和虚函数相关的东西,后来查了一下才知道是完全不相关的特性。特此记录一下。

虚函数

1. 写法

中的成员函数,定义时在最左侧加上virtual关键字,就被定义为了虚函数。

2. 意义

实现多态性。用父类指针指向子类对象时可以调用子类中的函数(前提:在父类中被定义为虚函数并在子类中进行了覆盖)。

3. 作用

基类中的虚函数可以在子类中被重新定义。使用基类指针指向子类对象并调用虚函数时,调用到的会是子类中的函数。
子类中重新实现的虚函数,其参数列表需要与基类中的虚函数相同。virtual修饰符加不加都可以。
子类的子类仍然可以重新定义该虚函数。

 #include <iostream>
 using namespace std;
 class A{
  public:
  virtual void func(){
   cout << "I am A." << endl;
  }
 };
 class B: public A{
  public:
  void func(){
   cout << "I am B." << endl;
  }
 };
 class C: public B{
  public:
  void func(){
   cout << "I am C." << endl;
  }
 };
 int main(int argc, const char * argv[]) {
  A* pointer = new C();
  pointer->func(); // 调用结果为: I am C.
  return 0;
 }

4. 原理

一旦类中有函数被定义为了虚函数,类就会拥有一个虚函数表
虚函数表中,保存着一系列函数指针,指向这个类中的虚函数实际的起始地址。
为了能获取到类的虚函数表,类的每个对象中就需要保存一个指针,指向自己的类的虚函数表(vtbl,也称虚表)。这个指针称为虚函数表指针(vptr)。
当使用基类指针指向子类对象并调用虚函数时,程序会通过对象的虚表指针去子类虚表中查找虚函数的实际地址。
也是因为这个原因,定义了虚函数的类的对象中会多一个4/8字节(取决于32位/64位)的指针,存于变量的起始位置。

虚继承

1. 写法

一个类型在继承另一个类时加上virtual修饰符,这个继承就是一个虚继承,其基类称为虚基类。

2. 意义

虚继承是为了解决C++中的多重继承问题而存在的特性。

3. 作用

如果一个子类继承了两个不同的基类,这两个类的继承链中又有相同的基类,那么在子类中,公共基类会被继承两次。
比如菱形继承:D继承了B、C,B和C又都是A的子类。
这样会导致两个后果:
一是浪费空间,公共基类的成员存在两份;
二是公共基类的成员来自两条不同的继承路径,在使用时不能确定使用的是哪一个,因此存在二义性。
虚继承可以解决这一问题。
由虚继承方式被继承的基类在子类的子类中只会存在一份内存结构。

4. 原理

虚继承中,子类并不像普通继承那样拥有一份基类的内存结构,而是加了一个虚基类表指针指向虚基类。
当虚基类的子类被继承时,虚基类表指针也会被继承。
因此,当某个子类通过不同路径继承了同一个虚基类时,该子类的内存中只会存在一份虚基类的拷贝,避免了冲突。

posted @ 2020-11-15 23:33  Zoey-L  阅读(443)  评论(0编辑  收藏  举报