基类构造函数和析构函数中调用的虚函数是基类的还是派生类的 ?

文章详细解释了在C++中,基类构造函数和析构函数内部调用虚函数的行为,指出在构造时调用的是基类自身函数,而在析构时则取决于实际类型。此外,文章还强调了虚函数在非静态成员函数中的动态行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(1)基类中的构造函数调用虚函数,调用的是基类自己的虚函数

 在编译的时候,函数调用就是静态编译的,而不是查虚函数表。

(2)基类中的析构函数调用虚函数,调用的是基类自己的虚函数

 在编译的时候,函数调用就是静态编译的,而不是查虚函数表。

(3)其它函数中调用虚函数(基类的函数和派生类的函数),需要通过查虚函数表来调用

1 基类中调用的虚函数

如下代码,Base是基类,其中有3个虚函数Do1,Do2和Do3,另外还有一个非虚函数Do。Derived 是继承Base的派生类,在Derived中覆写了Do1、Do2和Do3。在main函数中创建了一个Derived 对象,并把指针赋值给Base类型的指针b。

使用b调用Do1、Do2和Do3的时候,我们都知道调用的是Derived中的函数。使用b调用Do的时候,函数Do中调用了一个虚函数Do1,那么这里调用的Do1是基类Base中的函数还是派生类 Derived中的函数呢 ?  调用的是派生类中的Do1。

#include <iostream>
#include <string>

class Base {
public:
  void Do() {
    std::cout << "Base() Do()\n";
    Do1();
  }

  virtual void Do1() {
    std::cout << "Base() Do1()\n";
  }

  virtual void Do2() {
    std::cout << "Base() Do2()\n";
  }

  virtual void Do3() {
    std::cout << "Base() Do3()\n";
  }
};

class Derived : public  Base {
public:
  virtual void Do1() {
    std::cout << "Derived() Do1()\n";
  }

  virtual void Do2() {
    std::cout << "Derived() Do2()\n";
  }

  virtual void Do3() {
    std::cout << "Derived() Do3()\n";
  }
};

int main() {
  Base *b = new Derived;
  b->Do();
  std::cout << "----------------\n";
  b->Do1();
  b->Do2();
  b->Do3();
  return 0;
}

运行结果如下:

代码编译之后使用objdump -d -S -l来对a.out进行反汇编。

(1)调用非虚函数Do的时候,在汇编代码中是通过函数符号来表示的。通过符号表硬编码,调用的是父类中的Do。

(2)在调用虚函数Do1、Do2和Do3的时候,并不是通过符号来调用的。虚函数的调用是通过查虚表来调用的,从汇编指令中也可以看到,虚函数Do1在虚函数表的第一个位置;虚函数Do2在虚函数表的第二个表项,偏移0x8;虚函数Do3在虚函数表的第三个表项,偏移0x10。

(3)在Do中调用Do1,也是从虚函数表中找到虚函数,然后调用。

b 指向 Derived 类型的指针,其中的虚函数表是类 Derived 的,所以在 Do() 中调用的 Do1() 是 Derived 中的 Do1()。

2 基类的构造函数和析构函数调用虚函数

2.1 基类构造函数调用虚函数

如下代码 Base 是基类,Derived 是派生类。在基类 Base 的构造函数中调用虚函数 Do1,那么调用的是基类的 Do1 还是派生类的 Do1 呢 ?

基类构造函数调用的虚函数是基类中的虚函数。

派生类构造函数调用的虚函数是派生类中的虚函数。

如下代码,Base 是基类,Derived 是派生类。在基类的构造函数中调用了虚函数 Do1,在派生类的构造函数中调用了虚函数 Do1。

#include <iostream>
#include <string>
#include <stdlib.h>
#include <stdio.h>

class Base {
public:
  Base() {
    std::cout << "Base()\n";
    Do1();
    printf("Base(), vfptr = %p\n", (unsigned long *)*(unsigned long *)(void *)this);
  }

  virtual void Do1() {
    std::cout << "Base() Do1()\n";
  }

  void PrintVfptr() {
    printf("Base, vfptr = %p\n", (unsigned long *)*(unsigned long *)(void *)this);
  }
};

class Derived : public  Base {
public:
  Derived() {
    std::cout << "Derived()\n";
    Do1();
    printf("Derived(), vfptr = %p\n", (unsigned long *)*(unsigned long *)(void *)this);
  }

  virtual void Do1() {
    std::cout << "Derived() Do1()\n";
  }
};

int main() {
  Base *b = new Derived;
  b->PrintVfptr();
  return 0;
}

运行结果如下:

(1)从现象来看,可以看出基类的构造函数调用的虚函数是基类中实现的函数;派生类的构造函数中调用的虚函数是派生类中实现的虚函数。

(2)在基类和派生类的构造函数中打印了类的虚函数表。可以看出,在基类构造函数中的虚函数表是基类自己的虚函数表。

从下边汇编代码也可以看出来,在构造函数中调用虚函数,并没有查虚函数表,而是通过函数符号直接调用的本类的函数。

2.2 基类析构函数调用虚函数

从汇编代码中也可以看出来,基类析构函数中调用的是基类的虚函数,派生类析构函数中调用的是派生类的虚函数。

#include <iostream>
#include <string>
#include <stdlib.h>
#include <stdio.h>

class Base {
public:
  Base() {
    std::cout << "Base()\n";
  }

  virtual ~Base() {
    std::cout << "~Base()\n";
    Do1();
  }

  virtual void Do1() {
    std::cout << "Base() Do1()\n";
  }
};

class Derived : public  Base {
public:
  Derived() {
    std::cout << "Derived()\n";
  }

  ~Derived() {
    std::cout << "~Derived()\n";
    Do1();
  }

  virtual void Do1() {
    std::cout << "Derived() Do1()\n";
  }
};

int main() {
  Base *b = new Derived;
  delete b;
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值