- 父类没有虚函数
我们都知道子类在继承父类的时候,会将父类的变量和虚表指针继承下来保存到自己的内存里(即使父类private变量的内存也会继承下来,只会无法访问到),也就是子类对象内存中存储着一个父类对象。如果使菱形继承,那么"菱形"底部的类就会存储两份"菱形"顶端的类对象内存,通过虚继承可优化成一份,暂且不表。
class Base {
public: int a;
private: int b;
};
class Sub : public Base {
int c;
};
- 父类有虚函数
如果父类中有虚函数,那么就会有虚函数表存储在内存中,父类对象会生成一个虚表指针放在内存的开始位置。
class Base {
public: int a;
virtual void base_func() {}
virtual void base_func_other() {}
private: int b;
};
class Sub : public Base {
int c;
virtual void base_func() {}
virtual void sub_func() {}
};
- 多态类的继承在运行时,会拷贝一份虚函数表放到内存里(父类虚表仍存在),将虚表中重写过的方法地址用子类中的地址替换。同时子类对象会将父类对象的虚表指针继承下来,指向子类虚表。
- 因此子类虚表如图。
-
- 首先Sub_meta存储着运行时对象的类型信息(如果对象时Base类型就是Base_meta),运行时typeid会根据此来计算类型。
-
- base_func被子类重写了,因此换成子类中的函数地址。base_func_other仍然是基类中的地址。sub_func是子类新定义的虚函数,是子类中的地址。
- 灵活运用多态
变量不能表现多态,虚函数可以表现多态。
class Base {
public:
virtual void process() {
send_request();
get_response();
}
virtual void send_request() {}
virtual void get_response() {}
private: int b;
};
class Sub : public Base {
public:
virtual void process() {
Base::process();
}
virtual void send_request() {}
virtual void get_response() {}
};
Base* b = new Sub;
b->process();
首先会调用Base::process();这种指定了类名,会直接找到Base中process()的地址,执行send_request()和get_response()。然后编译器会去查这两个函数的地址,发现是虚函数,就从对象(Sub对象)虚表中查,最终调用Sub中的两个函数。