原创: 神秘编程 神秘编程 今天
问题抛出:为什么要使用多态?如果子类定义了与父类中原型相同的函数会发生什么?
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
那么C++是用什么实现多态的呢?
虚函数,抽象类,覆盖,模板(重载和多态无关)。
简单多态例子
#include<iostream>
using namespace std;
class Base
{
public:
Base(){}
virtual void foo()
{
cout<<"This is A."<<endl;
}
private:
int a;
int b;
};
class D: public Base
{
public:
D(){}
void foo()
{
cout<<"This is B."<<endl;
}
int x;
int y;
};
int main(int argc, char *argv[])
{
Base *a = new D();
a->foo();
if(a != NULL)
delete a;
return 0;
}
如果把virtual去掉,将显示:
This is A.
前面的多态通过使用虚函数virtual void foo()来实现。
到这里相信大家对多态有一丁点感觉了。
多态成立的个条件:
1:必须要有继承;
2:类中存在的成员函数是虚函数(virtual);
3:要有父类的指针或者引用指向子类对象;
4:子类一定要三同(返回值、函数名、参数列表);
5:二个类外;
(1)父类的成员函数返回值为 A*;子类的成员函数返回值为 B*,现在打破三同,还是覆盖。
(2)父类的析构函数一般都要加上(virtual),就可以利用多态性级联的调用析构函数,此时
先调用子类的,在调用父类的;(避免发生内存泄漏)。
内存模型
定义一个子类对象时,该子类就会有相应的内存情况,此时就是内存模型;
虚函数
虚函数是一个类的成员函数,其定义格式如下:virtual 返回值类型 函数名(参数表);
关键字virtual指明该成员函数为虚函数。
class Test()
{
public:
virtual void foo()
{
cout<<"This is A."<<endl;
}
}
虚函数剖析
(1)、当在类中出现一个(virtual),虚方法时,对象中第一个成员将是:_vptr;
此时只有一个成员,应该为4B,但是其因为有虚函数的存在,内部的第一个成员就一定为虚表指针;
指针32位下为4字节,所以此时一共为8字节;(不管内部有多少个虚函数,但是虚表指针只有一个);
(2)、虚函数将在继承体系中,一直为虚(覆盖时); 此时的virtual可以省略不写;
(3)、多态:就是对虚方法的重写;
(4)、虚表:装虚函数的表,其本质是地址的覆盖;
多态的原理
没有覆盖的虚表
如果子类有一个方法与父类的虚方法三同,此时覆盖;
但是通过父类的指针/引用永远只能访问父类对象的部分;
或取得虚表中的函数
Base b;
cout<<&b<<endl; //对象的地址
printf("%p\n", *(int *)(&b)); //虚表中虚表指针的值(指针4字节),所以转换整形指针,也就是虚表指针的地址;
((Fun)*((int*)*(int*)(&b) + 0))(); //Fun是函数指针,将获得虚表中的第一个函数;
((Fun)*((int*)*(int*)(&b) + 1))(); //Fun是函数指针,将获得虚表中的第二个函数;
((Fun)*((int*)*(int*)(&b) + 2))(); //Fun是函数指针,将获得虚表中的第三个函数;
多继承中虚表
父类均为虚函数,子类中也有虚函数,且没有进行覆盖,则将子类的放到第一个虚表的最后,其余的父类虚表就不用放了;因为,
就是放了,通过父类的指针/引用也访问不了,浪费内存空间;要是有覆盖的,则每个虚表都得画出;其余情况类似分析就行。
纯虚函数与抽象类
#include<iostream>
using namespace std;
class Test{
public:
virtual void fun() = 0; //这种形式就是存虚函数,赋值为0;
virtual void fun1() = 0;
virtual void fun2() = 0;
virtual void fun3() = 0;
};
int main(void){
return 0;
}
单继承下的虚函数表
//单继承下虚函数表:是如何组织的
class A{
public:
virtual void func(){
cout << "A::func" << endl;
}
virtual void funcA(){
cout << "A::funcA" << endl;
}
};
class B:public A{
public:
virtual void func(){
cout << "B::func" << endl;
}
virtual void funcB(){
cout << "B::funcB" << endl;
}
};
class C:public A{
public:
virtual void func(){
cout << "C::func" << endl;
}
virtual void funcC(){
cout << "C::funcC" << endl;
}
};
typedef void (*FUNC)();
int main()
{
A a;
B b;
C c;
cout << "A::虚表:" << endl;
((FUNC)(*(int *)(*(int*)(&a))))();
((FUNC)(*((int*)(*(int*)(&a)) + 1)))();
cout << "-------------------------------------" << endl;
cout << "B::虚表:" << endl;
((FUNC)(*(int *)(*(int*)(&b))))();
((FUNC)(*((int*)(*(int*)(&b)) + 1)))();
((FUNC)(*((int*)(*(int*)(&b)) + 2)))();
cout << "-------------------------------------" << endl;
cout << "C::虚表:" << endl;
((FUNC)(*(int *)(*(int*)(&c))))();
((FUNC)(*((int*)(*(int*)(&c)) + 1)))();
((FUNC)(*((int*)(*(int*)(&c)) + 2)))();
system("pause");
return 0;
}
单继承情况下:(虚函数的存放情况----->存放地址)
1:先基类的虚函数;
2:再派生类的虚函数;
3:存在覆盖的话,派生类的虚函数占据了覆盖的基类的虚函数位置;
typedef void(*FUNC)();
class A{
public:
virtual void func(){
cout << "A::func" << endl;
}
virtual void funcA(){
cout << "A::funcA" << endl;
}
private:
int a;
};
class B{
public:
virtual void func(){
cout << "B::func" << endl;
}
virtual void funcB(){
cout << "B::funcB" << endl;
}
private:
int b;
};
class C :public A, public B{
public:
virtual void func(){
cout << "C::func" << endl;
}
virtual void funcC(){
cout << "C::funcC" << endl;
}
private:
int c;
};typedef void(*FUNC)();
class A{
public:
virtual void func(){
cout << "A::func" << endl;
}
virtual void funcA(){
cout << "A::funcA" << endl;
}
private:
int a;
};
class B{
public:
virtual void func(){
cout << "B::func" << endl;
}
virtual void funcB(){
cout << "B::funcB" << endl;
}
private:
int b;
};
class C :public A, public B{
public:
virtual void func(){
cout << "C::func" << endl;
}
virtual void funcC(){
cout << "C::funcC" << endl;
}
private:
int c;
};
多继承条件下的虚函数表
//多继承条件下的虚函数表
void test()
{
C c;
cout << "多继承条件下的虚函数表:" << endl;
cout << "------------------------" << endl;
((FUNC)(*((int*)(*(int *)(&c)))))();
((FUNC)(*((int*)(*(int*)(&c)) + 1)))();
((FUNC)(*((int*)(*(int*)(&c)) + 2)))();
cout << "------------------------" << endl;
((FUNC)(*(int*)(*((int*)(&c) + 2))))();
((FUNC)(*((int*)(*((int*)(&c) + 2)) + 1)))();
}
多继承下的虚函数表------->有几个基类就有几张虚函数表-----派生类的虚函数存放在声明的基类的虚函数表中。
1:首先要声明基类的虚函数表
该类的成员变量(int a);
2:然后再声明基类虚的函数表
该类的成员变量(int b);
3:派生类中与基类造成覆盖的-->分别占据相应的位置-->C::func分别位于二个基类的虚函数形成覆盖。
4:派生类中未覆盖的,放在首先声明的基类的虚函数后面----->C::funC。
多继承条件下,基类指针指向派生类后,基类指针所能访问的函数
//多继承条件下,基类指针指向派生类后,基类指针所能访问的函数
void test1()
{
C c;
A *pa = &c;
B *pb = &c;
C *pc = &c;
cout << "基类指针pa所能调用的函数:" << endl;
pa->func();
pa->funcA();
//pa->funcB();error:提示类A没有成员funcB、funcC -->受到类型的限制
//pa->funcC();error
cout << "基类指针pb所能调用的函数:" << endl;
pb->func();
pb->funcB();
//pb->funcA();error
//pb->funcC();error
cout << "派生类指针pc所能调用的函数:" << endl;
pc->func();
pc->funcA();
pc->funcB();
pc->funcC();
}
多继承条件下,基类指针指向派生类对象后,基类指针之间强制类型转化之后,所能访问的函数
void test2()
{
C c;
A *pa = &c;
B *pb = &c;
C *pc = &c;
pa = reinterpret_cast<A *>(pb);
pa->func();
pa->funcA();
//pb = reinterpret_cast<B *>(pa);
//pb->func();
//pb->funcB();
//pa = reinterpret_cast<A *>(pc);
//pa->func();
//pa->funcA();
}
如果喜欢,欢迎关注微信公众号:神秘编程 一起讨论学习
专注Linux C/C++和算法知识学习分享总结
欢迎关注交流共同进步