一、C++多态
多态是指一组具有继承关系的类,拥有相同的接口(函数名、形参和返回值),并允许有各自不同的实现,且一个对象实例只有在调用这共同接口的时候,才能确定调用的是何种实现。 即,“一个接口,多种实现”
二、静态多态
编译器在编译期间完成的
静态多态 : 通过”彼此单独定义但支持共同操作的具体类“来表达共同性。
函数多态:即函数重载
概念:基于不同的参数列表,同一函数名可指向不同的函数体实现。
三、动态多态
动态绑定:在程序执行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
以类继承和虚函数机制为基础,表达共同接口。
动态多态是为面向对象编程服务的。因此,多态通常是指动态多态。
1、使用virtual关键字:
使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定
比如下面这个很有意思的例子:
class CWashRoom
{
public:
void GoToManWashRoom()
{
cout << "MAN--->Please Left" << endl;
}
void GoToWomanWashRoom()
{
cout << "WOMAN--->Please Right" << endl;
}
};
class CPerson
{
public:
virtual void GoToWashRoom(CWashRoom& _washRoom) = 0;//纯虚函数
};
class CMan :public CPerson
{
public:
virtual void GoToWashRoom(CWashRoom& _washRoom)
{
_washRoom.GoToManWashRoom();
}
};
class CWoman :public CPerson
{
public:
virtual void GoToWashRoom(CWashRoom& _washRoom)
{
_washRoom.GoToWomanWashRoom();
}
};
void Test()
{
CWashRoom washRoom;
for (int iIdx = 1; iIdx <= 10; ++iIdx)
{
CPerson* pPerson;
int iPerson = rand() % iIdx;
if (iPerson & 0x01)
{
pPerson = new CMan;
}
else
{
pPerson = new CWoman;
}
pPerson->GoToWashRoom(washRoom);
delete pPerson;
pPerson = NULL;
Sleep(1000);
}
}
2、虚函数的声明:
虚函数: 虚函数必须是被关键字virtual修饰的某类的非静态成员函数。
声明方法: virtual <函数返回值类型> <函数名>(<参数表>);
注意: 在派生类中重定义的虚函数必须和基类的虚函数完全相同(协变除外),即具有相同的返回类型、参数个数和参数类型。否则就是一种语法错误。
3、继承体系中容易搞混的三种同名函数的关系:
4、虚函数的总结:
为什么静态成员函数不能定义为虚函数
为什么构造函数不能定义为虚函数,为什么operator=可以定义成虚函数,但又不建议这样做
6、分析为什么构造函数和析构函数中不能调用虚函数,以及调用之后存在的问题
7、最好将析构函数声明为虚函数,为什么?
class Base
{
public:
Base* func()
{
cout << "Base::func1" << endl;
return this;
}
virtual~Base()
//~Base()
{
cout << "~Base()" << endl;
}
};
class Derive :public Base
{
public:
Derive* func()
{
cout << "Derive::func1" << endl;
return this;
}
~Derive()
{
cout << "~Derive()" << endl;
}
};
int main()
{
Base* ptr = new Derive;
delete ptr;
getchar();
return 0;
}
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base()" << endl;
}
private:
int _b;
};
class Derived :public Base
{
public:
Derived()
{
cout << "Derived()" << endl;
p = new int(0);
}
~Derived()
{
cout << "Derived()" << endl;
delete p;
}
private:
int _d;
int* p;
};
void Test()
{
Base* pb = new Derived;//如果析构函数不是虚函数,那么就是普通函数的调用方法,直接调用,不会去查函数虚表,这样pb是基类的指针,它只会调用基类的析构函数
delete pb;
}
int main()
{
Test();
getchar();
return 0;
}
验证结果:
四、虚函数表vftable(virtual function table):
先来看一段代码:
class Base
{
public:
virtual void FunTest1()
{
cout << "Base::FunTest1()" << endl;
}
private:
int _b;
};
class Derived :public Base
{
public:
virtual void FunTest1()
{
cout << "Derived::FunTest1()" << endl;
}
private:
int _d;
};
int main()
{
cout << sizeof(Derived) << endl;
return 0;
}
按照以前的算法 sizeof(D)是8,个字节,那么多出来的4个字节又是什么呢?
调用监视窗口可以看到有两个指针 _vfptr (虚表指针)
1、普通继承(单):
class B
{
public:
virtual void FunTest1()
{
cout << "B::FunTest1()" << endl;
}
virtual void FunTest2()
{
cout << "B::FunTest2()" << endl;
}
int _b;
};
class C :public B
{
public:
virtual void FunTest1()
{
cout << "C::FunTest1()" << endl;
}
virtual void FunTest3()
{
cout << "C::FunTest3()" << endl;
}
int _c;
};
void Test(B& b)
{
b.FunTest1();
b.FunTest2();
}
int main()
{
B b;
C c;
Test(b);
Test(c);
b._b = 1;
c._c = 2;
cout << sizeof(C) << endl;//12
getchar();
return 0;
}
2、多继承:
class B1
{
public:
virtual void FunTest1()
{
cout << "B1::FunTest1()" << endl;
}
virtual void FunTest2()
{
cout << "B1::FunTest2()" << endl;
}
int _b1;
};
class B2
{
public:
virtual void FunTest3()
{
cout << "B2::FunTest3()" << endl;
}
virtual void FunTest4()
{
cout << "B2::FunTest4()" << endl;
}
int _b2;
};
class C :public B1, public B2
{
public:
virtual void FunTest1()
{
cout << "C::FunTest1()" << endl;
}
virtual void FunTest3()
{
cout << "C::FunTest3()" << endl;
}
virtual void FunTest5()
{
cout << "C::FunTest5()" << endl;
}
int _c;
};
int main()
{
cout << sizeof(C) << endl;//20
B1 b1;
B2 b2;
C c;
B1* p1 = &b1;
p1->FunTest1();
p1->FunTest2();
cout << endl;
B2* p2 = &b2;
p2->FunTest3();
p2->FunTest4();
cout << endl;
C* pc = &c;
pc->FunTest3();
pc->FunTest5();
getchar();
return 0;
}
3、虚拟继承:
class B
{
public:
virtual void FunTest1()
{
cout << "B::FunTest1()" << endl;
}
virtual void FunTest2()
{
cout << "B::FunTest2()" << endl;
}
int b;
};
class C :virtual public B
{
public:
virtual void FunTest1()
{
cout << "C::FunTest1()" << endl;
}
virtual void FunTest3()
{
cout << "C::FunTest3()" << endl;
}
int c;
};
int main()
{
cout << sizeof(C) << endl;//20
B b;
C c;
b.b = 1;
c.c = 2;
B* p1 = &b;
p1->FunTest1();
p1->FunTest2();
cout << endl;
C* pc = &c;
pc->FunTest1();
pc->FunTest3();
getchar();
return 0;
}
4、菱形继承:
class B
{
public:
virtual void FunTest1()
{
cout << "B::FunTest1()" << endl;
}
virtual void FunTest2()
{
cout << "B::FunTest2()" << endl;
}
int b;
};
class C1 :virtual public B
{
public:
virtual void FunTest1()
{
cout << "C1::FunTest1()" << endl;
}
virtual void FunTest3()
{
cout << "C1::FunTest3()" << endl;
}
int c1;
};
class C2 :virtual public B
{
public:
virtual void FunTest1()
{
cout << "C2::FunTest1()" << endl;
}
virtual void FunTest4()
{
cout << "C2::FunTest4()" << endl;
}
int c2;
};
class D :public C1, public C2
{
public:
virtual void FunTest1()
{
cout << "D::FunTest1()" << endl;
}
virtual void FunTest3()
{
cout << "D::FunTest3()" << endl;
}
virtual void FunTest5()
{
cout << "D::FunTest5()" << endl;
}
int d;
};
int main()
{
cout << sizeof(D) << endl;
getchar();
return 0;
}
这次为什么D的大小是36 呢?
这时我们就需要分析其对象模型
虚表指针:
每一个多态类都有且仅有一个存储所有虚函数入口地址的数组。
虚函数表指针vptr:
每一个多态类的所有对象都有且仅有一个的隐藏成员vptr,指向虚函数表vftable.
vptr和vftable的初始化:
基类对象的vptr指向基类的vftable。
派生类对象vptr指向该派生类的vftable。
如果派生类没有覆盖(override)父类的虚函数,则其vftable中对应表项指向其父类的此函数。 如果派生类覆盖了父类的虚函数,则vftable中对应表项指向重写后的此函数。 如果派生类定义了新的虚函数,则此函数的地址将被添加到vftable中
虚函数的工作原理:
多态的函数调用语句被编译成一系列根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的过程。
vptr和vftable在内存中的存储
class Base
{
public:
virtual void func1()
{
cout << "Base::func1" << endl;
}
virtual void func2()
{
cout << "Base::func2" << endl;
}
void func3()
{
cout << "Base::func3" << endl;
}
};
class Derive :public Base
{
public:
virtual void func1()
{
cout << "Derive::func1" << endl;
}
virtual void func3()//构成重载 func也要放在虚表中
{
cout << "Derive::func3" << endl;
}
virtual void func4()
{
cout << "Derive::func3" << endl;
}
};
typedef void(*V_FUNC)();
void PrintVTable(int vtable)//打印虚表
{
int* vf_array = (int*)vtable;
printf("vtable:0x%p\n",vtable);
for (size_t i = 0; vf_array[i] != 0; ++i)
{
printf("vatable[%d]:0x%p->", i, vf_array[i]);
V_FUNC f = (V_FUNC)vf_array[i];
f();
}
printf("----------------------------\n");
}
int main()
{
Base b;
Derive d;
PrintVTable(*(int*)&b);
PrintVTable(*(int*)&d);
Base* ptr=&d;
ptr->func1();//构成多态
ptr->func3();//不是多态
getchar();
return 0;
}