C++对象模型之多态
所谓的多态,其实就是多种形态,简单地说当使用
父类的指针/引用调用重写的虚函数,当父类的指针/引用指向父类的对象时调用的是父类的虚函数,指向子类对象时调用的是子类的虚函数。
虚函数–类的成员函数前面加virtual关键字,则这个成员函数称为虚函数。
虚函数的重写–当在子类定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写(覆盖)了父类的这个虚函数。
class Person{public:{
public:
virtual void BuyTickets()
{
cout<<"买票"<<endl;
}
}
class Student : public Person
{
public:
virtual void BuyTickets()
{
cout<<"买半价票"<<endl;
}
};
void Fun(Person& p)
{
p.BuyTickets();
}
void test()
{
Person p;
Student s;
Fun(p);
Fun(s);
}`
多态的实现机制—虚表
虚表又叫虚函数表,是通过一块连续内存来存储函数的地址,这张表解决了继承,虚函数(重写)的问题,在有虚函数的对象实例中都存在一种虚函数表,虚函数表就像一个地图,指明了实际应该调用的虚函数。
class Base
{
public:
virtual void f1()
{
}
virtual void f2()
{
}
private:
int a;
};
void test()
{
Base b1;
}
虚函数表
无虚函数覆盖的继承
class Base{
public:
virtual void func1()
{
cout << "Base::func1" << endl;
}
virtual void func2()
{
cout << "Base::func2" << endl;
}
private:
int a;
};
class Derive :public Base
{
public:
virtual void func11()
{
cout << "Derive::func1" << endl;
}
virtual void func3()
{
cout << "Derive::func3" << endl;
}
virtual void func4()
{
cout << "Derive::func4" << endl;
}
private:
int b;
};
在这个继承关系中,子类没有重写父类的任何虚函数,那么在派生类的实例化中其虚函数表如下所示。
(1)虚函数按照其声明顺序放在表中
(2)父类的虚函数在子类的虚函数前面
探索单继承对象模型
class Base
{
public:
virtual void f1()
{
cout<<"Base::f1"<<endl;
}
virtual void f2()
{
cout<<"Base::f2"<<endl;
}
private:
int a;
};
class Derive : public Base
{
public:
virtual void f1()
{
cout<<"Derive::f1"<<endl;
}
virtual void f3()
{
cout<<"Derive::f3"<<endl;
}
virtual void f4()
{
cout<<"Derive::f4"<<endl;
}
private:
int b;
};
为了更清楚的分析,我们试图把虚函数表打印出来。
typedef void(*FUNC)();
void PrintVTable(int *VTable)
{
cout<<"虚函数表地址"<<endl;
for(int i = 0;VTable[i] != 0;++i)
{
printf("第%个虚函数地址:0x%p->",i,VTable[i]);
FUNC f = (FUNC)VTable[i];
f();
}
cout<<"----------------"<<endl;
}
void Test()
{
Base b1;
Derive d1;
int* VTable1 = (int*)(*(int*)&d1);
int* VTable2 = (int*)(*(int*)&b1);
PrintVTable(VTable1);
PrintVTable(VTable2);
}
编译环境 VS2008 IDE 监视窗口中的虚表
(1)可以看见,派生类Derive::f1重写基类Base::f1,覆盖了相应虚表位置上的函数。
(2)这里没有看到派生类的f3 and f4,这两的函数就放在函数f2 的后面。(由于编译器的原因无法直接看见)。
虚函数表以0结尾。
(3)覆盖的函数被放到原来父类函数的位置。
(4)没有被覆盖的函数依旧。
Base* b = new Derive();
b->f1();
由b所指的内存中的虚函数表f1()的位置已经被Derive f1()地址所取代,于是在实际调用发生时,是Derive::f1()被调用了。这就实现了多态。
多态条件
(1)虚函数的重写。
(2)父类的指针或引用调用虚函数。
多重继承的内存布局
无虚函数的覆盖
下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。注意:子类并没有覆盖父类的函数。
对于子类实例中的虚函数表,是下面这个样子。
(1)每个父类都有自己的虚表。
(2)子类没有继承的成员函数放到了第一个父类的表中。(所谓第一个父类是按照声明的顺序来判断)
这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。
class Base1
{
public:
virtual void func1()
{
cout<<"Base1::func1"<<endl;
}
virtual void func2()
{
cout<<"Base1::func2"<<endl;
}
private:
int b1;
};
class Base2
{
public:
virtual void func1()
{
cout<<"Base2::func1"<<endl;
}
virtual void func2()
{
cout<<"Base2::func2"<<endl;
}
private:
int b2;
};
class Derive : public Base1,public Base2
{
public:
virtual void func1()
{
cout<<"Derive::func1"<<endl;
}
virtual void func3()
{
cout<<"Derive::func3"<<endl;
}
private:
int d1;
};
typedef void (*FUNC)();
void PrintVTable(int* VTable)
{
cout<<"虚表地址"<<VTable<<endl;
for(int i = 0;VTable[i] != 0;++i)
{
printf("第%个虚函数地址:0x%p->",i,VTable[i]);
FUNC f = (FUNC)VTable[i];
f();
}
cout<<"-------------"<<endl;
}
void test()
{
Derive d1;
int* VTable = (int*)(*(int*)&d1);
PrintVTable(VTable);
//Base2虚函数表在对象Base1后面
VTable = (int*)(*((int*)&d1 + sizeof(Base1)/4));
PrintVTable(VTable);
}
Derive d;
Base1* b1 = &d;
Base2* b2 = &d;
b1->func1();
b2->func2();
菱形继承
无虚函数的重写
其类继承的源代码如下所示。其中,每个类都有两个变量,一个是整形(4字节),一个是字符(1字节),而且还有自己的虚函数,自己overwrite父类的虚函数。如子类D中,f()覆盖了超类的函数, f1() 和f2() 覆盖了其父类的虚函数,Df()为自己的虚函数。
class B
{
public:
int ib;
char cb;
public:
B():ib(0),cb('B') {}
virtual void f() { cout << "B::f()" << endl;}
virtual void Bf() { cout << "B::Bf()" << endl;}
};
class B1 : public B
{
public:
int ib1;
char cb1;
public:
B1():ib1(11),cb1('1') {}
virtual void f() { cout << "B1::f()" << endl;}
virtual void f1() { cout << "B1::f1()" << endl;}
virtual void Bf1() { cout << "B1::Bf1()" << endl;}
};
class B2: public B
{
public:
int ib2;
char cb2;
public:
B2():ib2(12),cb2('2') {}
virtual void f() { cout << "B2::f()" << endl;}
virtual void f2() { cout << "B2::f2()" << endl;}
virtual void Bf2() { cout << "B2::Bf2()" << endl;}
};
class D : public B1, public B2
{
public:
int id;
char cd;
public:
D():id(100),cd('D') {}
virtual void f() { cout << "D::f()" << endl;}
virtual void f1() { cout << "D::f1()" << endl;}
virtual void f2() { cout << "D::f2()" << endl;}
virtual void Df() { cout << "D::Df()" << endl;}
};
我们用来存取子类内存布局的代码如下所示:(在VC++ 2003和G++ 3.4.4下)
typedef void(*Fun)(void);
int** pVtab = NULL;
Fun pFun = NULL;
D d;
pVtab = (int**)&d;
cout << "[0] D::B1::_vptr->" << endl;
pFun = (Fun)pVtab[0][0];
cout << " [0] "; pFun();
pFun = (Fun)pVtab[0][1];
cout << " [1] "; pFun();
pFun = (Fun)pVtab[0][2];
cout << " [2] "; pFun();
pFun = (Fun)pVtab[0][3];
cout << " [3] "; pFun();
pFun = (Fun)pVtab[0][4];
cout << " [4] "; pFun();
pFun = (Fun)pVtab[0][5];
cout << " [5] 0x" << pFun << endl;
cout << "[1] B::ib = " << (int)pVtab[1] << endl;
cout << "[2] B::cb = " << (char)pVtab[2] << endl;
cout << "[3] B1::ib1 = " << (int)pVtab[3] << endl;
cout << "[4] B1::cb1 = " << (char)pVtab[4] << endl;
cout << "[5] D::B2::_vptr->" << endl;
pFun = (Fun)pVtab[5][0];
cout << " [0] "; pFun();
pFun = (Fun)pVtab[5][1];
cout << " [1] "; pFun();
pFun = (Fun)pVtab[5][2];
cout << " [2] "; pFun();
pFun = (Fun)pVtab[5][3];
cout << " [3] "; pFun();
pFun = (Fun)pVtab[5][4];
cout << " [4] 0x" << pFun << endl;
cout << "[6] B::ib = " << (int)pVtab[6] << endl;
cout << "[7] B::cb = " << (char)pVtab[7] << endl;
cout << "[8] B2::ib2 = " << (int)pVtab[8] << endl;
cout << "[9] B2::cb2 = " << (char)pVtab[9] << endl;
cout << "[10] D::id = " << (int)pVtab[10] << endl;
cout << "[11] D::cd = " << (char)pVtab[11] << endl;
程序运行结果如下: