参考:http://blog.youkuaiyun.com/haoel/article/details/1948051/
#include<iostream>
#include<string>
using namespace std;
class Base
{
public:
virtual void f(){ cout << "Base::f" << endl; }
virtual void g(){ cout << "Base::g" << endl; }
virtual void h(){ cout << "Base::h" << endl; }
};
int main()
{
typedef void(*Fun)(void);
Base b;
Fun pFun = NULL;
cout << "虚函数表的地址" << (int*)(&b) << endl;
cout << "虚函数表-第一个函数的地址" << (int*)*(int*)(&b) << endl;
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 1);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 2);
pFun();
return 0;
}
一般继承,没有虚函数覆盖
假设继承关系如下图所示:
注意点
1、在这个继承关系中,子类没有重载任何父类函数。那么,在派生类的实例中,虚函数表如下图所示:
对于实例:Derive d;的虚函数表如下图所示:
#include<iostream>
#include<string>
using namespace std;
class Base
{
public:
virtual void f(){ cout << "Base::f" << endl; }
virtual void g(){ cout << "Base::g" << endl; }
virtual void h(){ cout << "Base::h" << endl; }
};
class Derived:public Base
{
virtual void f1(){ cout << "Base::f1" << endl; }
virtual void g1(){ cout << "Base::g1" << endl; }
virtual void h1(){ cout << "Base::h1" << endl; }
};
int main()
{
typedef void(*Fun)(void);
//Base b;
Derived b;
Fun pFun = NULL;
cout << "虚函数表的地址" << (int*)(&b) << endl;
cout << "虚函数表-第一个函数的地址" << (int*)*(int*)(&b) << endl;
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 1);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 2);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 3);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 4);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 5);
pFun();
return 0;
}
一般继承(有虚函数覆盖)
子类虚函数重载父类虚函数
对于派生实例
注意点
1)派生类的f()函数被放到了原来基类函数的位置
2)其他函数依旧如此
#include<iostream>
#include<string>
using namespace std;
class Base
{
public:
virtual void f(){ cout << "Base::f" << endl; }
virtual void g(){ cout << "Base::g" << endl; }
virtual void h(){ cout << "Base::h" << endl; }
};
class Derived:public Base
{
virtual void f1(){ cout << "Base::f1" << endl; }
virtual void g1(){ cout << "Base::g1" << endl; }
virtual void h1(){ cout << "Base::h1" << endl; }
};
int main()
{
typedef void(*Fun)(void);
//Base b;
Derived b;
Fun pFun = NULL;
cout << "虚函数表的地址" << (int*)(&b) << endl;
cout << "虚函数表-第一个函数的地址" << (int*)*(int*)(&b) << endl;
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 1);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 2);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 3);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 4);
pFun();
/*pFun = (Fun)*((int*)*(int*)(&b) + 5);
pFun();*/
return 0;
}
则就能实现用基类的指针指向派生类的函数
Base *b=new Derived();
b->fun();
由于对象b的虚函数表中f()的位置已经被派生类的f()所取代,于是在实际调用发生的时候,则运行的是派生类的函数,就实现了多态。
多重继承(无虚函数覆盖)
对于子类实例中的虚函数表,是下面这个样子:
总结
1)每个基类都有自己的虚表
2)子类的成员函数被放到了第一个父类的表中
这样做的目的:
解决不同的父类类型的指针指向同一个自雷示例,从而能调用到实际的函数
#include<iostream>
#include<string>
using namespace std;
class Base1
{
public:
virtual void f(){ cout << "Base::f" << endl; }
virtual void g(){ cout << "Base::g" << endl; }
virtual void h(){ cout << "Base::h" << endl; }
};
class Base2
{
public:
virtual void f(){ cout << "Base::f" << endl; }
virtual void g(){ cout << "Base::g" << endl; }
virtual void h(){ cout << "Base::h" << endl; }
};
class Base3
{
public:
virtual void f(){ cout << "Base::f" << endl; }
virtual void g(){ cout << "Base::g" << endl; }
virtual void h(){ cout << "Base::h" << endl; }
};
class Derived :public Base1,Base2,Base3
{
virtual void f1(){ cout << "Base::f1" << endl; }
virtual void g1(){ cout << "Base::g1" << endl; }
virtual void h1(){ cout << "Base::h1" << endl; }
};
int main()
{
typedef void(*Fun)(void);
//Base b;
Derived b;
Fun pFun = NULL;
cout << "虚函数表的地址" << (int*)(&b) << endl;
cout << "虚函数表-第一个函数的地址" << (int*)*(int*)(&b) << endl;
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 1);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 2);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 3);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 4);
pFun();
/*pFun = (Fun)*((int*)*(int*)(&b) + 5);
pFun();*/
Base1 *ptr1 = new Derived();
ptr1->f();
Base2 *ptr2 = new Derived();//错误不允许对不可访问的基类进行转换
ptr1->f();
Base3 *ptr3 = new Derived();//错误不允许对不可访问的基类进行转换
ptr1->f();
return 0;
}
e:\c++\project\多重继承(无虚函数覆盖)\多重继承(无虚函数覆盖)\源.cpp(58): error C2243: “类型转换”: 从“Derived ”到“Base2 ”的转换存在,但无法访问
可以的:
Derived b;
Base1 *ptr1 = new Derived();
ptr1->f();
Base2 *ptr2 =NULL;
ptr1->f();
/*Base3 *ptr3 = new Derived();
ptr1->f();
*/
return 0;
结果Base::1
Base::1
多重继承(有虚函数覆盖)
面我们再来看看,如果发生虚函数覆盖的情况。
下图中,我们在子类中覆盖了父类的f()函数
下面是对于子类实例中的虚函数表的图
#include<iostream>
#include<string>
using namespace std;
class Base1
{
public:
virtual void f(){ cout << "Base::f" << endl; }
virtual void g(){ cout << "Base::g" << endl; }
virtual void h(){ cout << "Base::h" << endl; }
};
class Base2
{
public:
virtual void f(){ cout << "Base::f" << endl; }
virtual void g(){ cout << "Base::g" << endl; }
virtual void h(){ cout << "Base::h" << endl; }
};
class Base3
{
public:
virtual void f(){ cout << "Base::f" << endl; }
virtual void g(){ cout << "Base::g" << endl; }
virtual void h(){ cout << "Base::h" << endl; }
};
class Derived :public Base1,Base2,Base3
{
virtual void f(){ cout << "Base::f1" << endl; }
virtual void g1(){ cout << "Base::g1" << endl; }
virtual void h1(){ cout << "Base::h1" << endl; }
};
int main()
{
typedef void(*Fun)(void);
//Base b;
Derived b;
Fun pFun = NULL;
cout << "虚函数表的地址" << (int*)(&b) << endl;
cout << "虚函数表-第一个函数的地址" << (int*)*(int*)(&b) << endl;
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 1);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 2);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 3);
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 4);
pFun();
/*pFun = (Fun)*((int*)*(int*)(&b) + 5);
pFun();*/
Base1 *ptr1 = new Derived();
ptr1->f();
Base2 *ptr2 = new Derived();
ptr1->f();
Base3 *ptr3 = new Derived();
ptr1->f();
return 0;
}
1>e:\c++\project\多重继承(无虚函数覆盖)\多重继承(无虚函数覆盖)\源.cpp(58): error C2243: “类型转换”: 从“Derived ”到“Base2 ”的转换存在,但无法访问
这样可以
typedef void(*Fun)(void);
Derived b;
Base1 *ptr1 = new Derived();
ptr1->f();
Base2 *ptr2 =NULL;
ptr1->f();
/*Base3 *ptr3 = new Derived();
ptr1->f();
*/
return 0;
安全性:
一、通过父类型的指针访问子类自己的虚函数
我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数:
Base1 *b1 = new Derive();
b1->f1(); //编译出错
任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。(关于这方面的尝试,通过阅读后面附录的代码,相信你可以做到这一点)
二、访问non-public的虚函数
另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-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();
}