2017.7.21
虚函数是C++多态性的主要组成部分。下面我简单地从单继承分析虚表具体的内存结构。
先列举一个实例。
#include <iostream>
class base {
public:
base() {}
base(int i) {
this->i = i;
}
void print() {
std::cout << "base print" << std::endl;
}
virtual void print_v() {
std::cout << "base print_v" << std::endl;
}
private:
int i;
};
class derived: public base{
public:
derived() {}
derived(int i, int j): base(i) {
this->j = j;
}
void print() {
std::cout << "derived print" << std::endl;
}
virtual void print_v() {
std::cout << "derived print_v" << std::endl;
}
private:
int j;
};
int main( int argc, char *argv[]) {
base* instance1 = new derived(1, 2);
instance1->print();
instance1->print_v();
return 0;
}
图1为上述代码的结果图
可以发现base类的指针指向派生类对象,调用非虚函数print()时,实际调用了base::print();调用虚函数print_v(),实际调用了derived::print_v(),那么原因是什么呢?首先我们需要了解一下虚表的结构。
![图2 base和derived情况](https://img.haomeiwen.com/i1215789/3eddc63b753cc1d6.png?
![Uploading IMG_1429_169073.JPG . . .]
imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
图2 右侧代表虚指针的地址
虚表属于每一个类,类实例化后对象的首地址存有虚指针,该指针指向虚表的首地址。在上述代码中添加一行代码
typedef (void)(*fun)(void);
fun f1 = (fun)*(*((uint64_t **)instance1));
f1()
输出为
derived print_v
这里需要注意,转化类型应该符合该操作系统的内存结构。这里环境是macos sierra
图3 覆盖的内容然而非虚成员函数没有被覆盖,因为成员函数并没有保存在对象中,编译期确定,并静态或动态绑定到对应的对象上。
所以base类型的指针调用的是继承类的虚函数
对象实例的虚指针指向对应的虚表,虚表中的虚函数是定义类时就形成的。
ps: 虚表结构
包括vptr,RTTI, top offset,接下来我将对多重继承和虚表结构深入理解。
网友评论