c++ 虚函数常见问题

1 虚函数,虚表基础

虚函数,虚表基础

2 虚函数表保存在哪里 ?

虚函数表在编译的时候确定。在 linux 下,保存在只读数据段的重定位段,这个段的名字是 .data.rel.ro。

如下代码,编译之后,使用 readelf -t a.out 可以查看看到这一段的大小是 0x68,如果从代码中删除一个虚函数,比如把函数 Do2() 不用 virtual 修饰,可以看到这个段的大小减小了 16。由此,可以验证虚函数表保存在了 .data.rel.ro。

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

class Base {
public:
  Base() {
  }

  ~Base() {
  }

  virtual void Do1(int d) {
    std::cout << d << std::endl;
  }

  virtual void Do2(int d) {
    std::cout << d << std::endl;
  }
};

class Derived : public  Base {
public:
  Derived() {
  }

  ~Derived() {
  }

  virtual void Do1(int d) {
    std::cout << d << std::endl;
  }

  virtual void Do2(int d) {
    std::cout << d << std::endl;
  }
};


int main() {
  Base *b = new Derived();
  b->Do1(1);
  b->Do2(2);
  delete b;
  return 0;
}

将 Base() 和 Derived() 中的函数 Do2() 不用 virtual 修饰,可以看到 .data.rel.ro 的大小减小了 16。

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

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

3 哪些函数不能声明为虚函数 ?

3.1 构造函数

构造函数声明为虚函数,编译错误如下。

访问虚函数首先要通过对象的地址找到虚函数表的地址,然后找到对应的虚函数来执行。也就是说虚函数的调用依赖于对象。而调用构造函数的时候,这个对象还不存在,所以构造函数不能声明为虚函数。

3.2 静态函数

静态函数声明为虚函数,编译错误如下。

与构造函数类似,静态函数中也没有 this 指针,所以静态函数不能声明为虚函数。

3.3 函数模板

函数模板不能声明为虚函数。编译报错如下。虚函数表需要在类定义的时候就要确定,对于函数模板来说,类定义的时候,这个函数有多少实例是不确定的,所以函数模板不能定义为虚函数。

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

class Base {
public:
  Base() {
  }

  ~Base() {
  }

  template <class T>
  virtual void Do1(T d) {
    std::cout << d << std::endl;
  }

  void Do2(int d) {
    std::cout << d << std::endl;
  }
};

int main() {
  Base *b = new Base();
  b->Do1(1);
  b->Do2(2);
  delete b;
  return 0;
}

函数模板不能声明为虚函数。但是类模板中的函数可以声明为虚函数,因为对于类模板来说,在定义一个实例的时候,这个类的定义就是确定的,虚函数表的大小也是确定的。

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

template <class T>
class Base {
public:
  Base() {
  }

  ~Base() {
  }

  virtual void Do1(T d) {
    std::cout << d << std::endl;
  }

  void Do2(int d) {
    std::cout << d << std::endl;
  }
};

int main() {
  Base<int> *b = new Base<int>();
  b->Do1(1);
  b->Do2(2);
  delete b;
  return 0;
}

4 inline 函数可以声明为虚虚函数

如下代码,Base 是基类,其中有一个 inline 虚函数 Do1()。Derived1 和 Derived2 是 Base 的派生类。分别创建 Base、Derived1、Derived2 的一个对象,然后通过指针调用函数 Do1()。代码编译没有问题,运行也没有问题。

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

class Base {
public:
  virtual ~Base() {
  }

  virtual inline void Do1(int d) {
    std::cout << d << std::endl;
  }
};

class Derived1 : public Base{
public:
  virtual inline void Do1(int d) {
    std::cout << d << std::endl;
  }
};

class Derived2 : public Base{
public:
  virtual inline void Do1(int d) {
    std::cout << d << std::endl;
  }
};

void Do(Base *b, int data) {
  b->Do1(data);
}

int main() {
  Base *b = new Base();
  Base *b1 = new Derived1();
  Base *b2 = new Derived2();

  Do(b, 10);
  Do(b1, 20);
  Do(b2, 30);
  return 0;
}

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值