1.那些被virtual关键字修饰的成员函数,就是虚函数。
2.虚函数的作用-----------实现多态性(Polymorphism),多态性是将接口与实现进行分离;
多态还有个关键之处就是用指向基类的指针或引用来操作对象
总结:指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
虚函数(Virtual Function)来实现
1. 由于这两个类中有虚函数存在,所以编译器就会为他们两个分别创建一个虚表vtbl,每个类都有自己的vtbl,保存自己类中虚函数的地址,解决了继承、覆盖的问题;
1)虚函数按照其声明顺序放于表中。
2)父类的虚函数在子类的虚函数前面。
3)虚表的第一项是RTTI信息。
2. 每个类的对象有一个虚指针vptr,指向虚表。
1)C++<span times="" new="" ';="" mso-hansi-font-family:="" 'times="" "="">的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。
2)构造函数附加一些代码,初始化vptr,其位置位于:基类初始化后,程序代码或初始化列表前;
拷贝构造函数和赋值操作符中,会安插代码,设置vptr;
析构函数中,重新设置vptr,使之指向相应的基类。(注:派生类对象析构时,会依次变成相应的基类对象)
例如:
class Base {
public:
virtual void f() { cout<< "Base::f" << endl; }
virtual void g() { cout<< "Base::g" << endl; }
virtual void h() { cout<< "Base::h" << endl; }
};
通过类实例得到虚表指针:
1)覆盖的函数被放到了虚表中原来父类虚函数的位置。
2)没有被覆盖的函数依旧。
1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。
2)没有被覆盖的函数依旧。
这样,我们就可以看到对于下面这样的程序,
Base *b = new Derive();
b->f();
由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。
1. 多个虚函数表
注意:子类并没有覆盖父类的函数。
1) 每个父类都有自己的虚表。
2) 子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)
子类实例中的虚函数表:
三个父类虚函数表中的f()的虚函数
如果父类的虚函数是private的,但这些非public的虚函数。
如:
class Base {
private:
virtual void f() { cout << "Base::f" << endl; }
};
class Derive : public Base{
};
typedef void(*Fun)(void);
void main() {
Derive d;
Fun pFun = (Fun)*((int*)*(int*)(&d)+0);
pFun();
}
以上出自: http://haoel.blog.51cto.com/313033/1245951)虚表的第1项是类的RTTI信息;
2) 多重继承中,针对第二或后继的基类,需要在执行期调整this指针
1)派生类含有虚函数表,基类对象虚表,2个表
2)point2d ,point3d对象相互转换时,需要调整this指针
ptr->z()
1,虽然并不知道ptr所指对象的真正类型,但经由ptr可以取到该对象的虚表
2,虽然不知道哪一个z()函数被调用,但知道每一个z()函数地址被放到
slot4(即虚表的第4项)
于是,编译器将该调用转换为:
(*ptr->vptr[4])(ptr);
vptr:编译器安插的指针,指向虚表
4:z()在虚表中的位置,即slot编号
唯一执行期才知道的东西是:slot4所指的到底是哪一个z()函数
派生类对象间赋值----不发生切割:
派生类对象赋值给基类对象----发生切割
实际而言,安全;vptr能够保证在成员初始化列表之前被设定好。
语义上可能不安全,因为函数本身可能依赖未设立初值的成员
但当给基类构造函数(baseclass constructor)提供参数时,在类的constructor的成员初始化列表中调用该class的虚函数,是不安全的。
因为:vptr不是未设定好,就是被设定指向错误的class,进一步讲,该函数所存取的任何类的数据成员一定没有被初始化。
————————————————————————————————————————————
1虚基类的构造
2基类的构造
3vptr的设置
4程序员的代码
析构函数以其相反的顺序析构!!!!!!!!!!!!!111
目的:为了将具有继承关系的对象放入容器,而且操作时不发生切割。
方法一:对象放入智能指针shared_ptr中,
例如:vector<shared_ptr<基类>>
方法二:使用boost中的指针容器