C++多态和虚表浅析
来源:互联网 发布:备案的域名 编辑:程序博客网 时间:2024/06/16 17:43
对象的类型两种:什么是动态类型?什么是静态类型?
1静态类型:对象声明时的类型,在编译时确定
2动态类型:目前所指对象的类型,在运行时确定
动态是用new动态申请内存的~~new出来的内存需要手动释放~~
比如说 int a=new int(10)~也就是用delete来释放~~
另外~动态分配的内存是在堆上面的~~~内存有~堆 栈 和常量存储区~
静态是一般的类型~~比如说int a=10;内存分配在栈上~~
另外栈内存上的数据是有顺序的~~先进后出~~
而在堆内存上是无序的~~所以需要指针来操作~~万一指针丢了~~或者忘记释放内存~是很麻烦的~~
(二) 多态分为静态多态和动态多态
1静态多态含有函数重载和泛型编程
静态多态:编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编错误。
#include<iostream>
using manespace std;
int Add(int left ,int right)
{
return left + right;
}
float Add(float left ,float right)
{
return left + right;
}
int main
{
cout<<"Add(10,20)"<<endl;
cout<<"Add(10.33f,22.34f)"<<endl;
return 0;
}
运行结果是30
32.67
像这种重载就属于静态多态
动态多态含虚函数:
动态绑定:在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。
使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。
#include<iostream>
#include<Windows.h>
using namespace std;
class WashRoom
{
public:
void GOTOMANwashroom()
{
cout<<"男人左转"<<endl;
}
void GOTOWOMANwashroom ()
{
cout<<"女人右转"<<endl;
}
}
class Person
{
public:
virtual void GOTOwashroom(WashRoom& washroom)=0;
};
class MAN:public Person
{
virtual voidGOTOwashroom(WashRoom& washroom)
{
washroom.GOTOMANwashroom();
}
};
class WOMAN:public Person
{
virtual voidGOTOwashroom(WashRoom& washroom)
{
washroom.GOTOWOMANwashroom();
}
};
void FunTest()
{
WashRoom washroom;
for ( int iIdx = 1 ; iIdx <= 10 ; ++iIdx)
{
Person* pPerson;
int iPerson = rand ()%iIdx;
if (iPerson&0x01)
{
pPerson = new MAN ;
}
else
{
pPerson = new WOMAN ;
}
pPerson->GOTOMANwashroom(washroom);
delete pPerson;
pPerson = NULL ;
Sleep(1000 );
}
}
int main()
{
FunTest();
return 0;
}
输出 男人左转 女人右转 男人左转 女人右转 男人左转 女人右转 男人左转 女人右转 男人左转 女人右转 ,每个输出之间间隔1秒左右
【动态绑定条件】
1、必须是虚函数2、通过基类类型的引用或者指针调用虚函数
(三)先看一段代码
#include<iostream>
using namespace std;
class animal
{
public :
virtual void eat()
{
cout << "animal eat" << endl;
}
void sleep()
{
data1 = 10;
cout << "this=" << this << endl;
cout << "animal sleep" << endl;
}
private:int data1;
};
class dog:public animal
{
virtual void eat()
{
cout << "dog eat" << endl;
}
void sleep()
{
cout << "dog sleep" << endl;
}
private:int data2;
};
int main()
{
dog d;
animal *a = &d;
a->eat();
a->sleep();
cout << sizeof (animal) << endl;
cout << sizeof(dog) << endl;
system("pause");
return 0;
}
输出结果是
第一个输出是因为
C++编译器在编译的时候,要确定每个对象调用的函数(要求此函数是非虚函数)的地址,这称为早期绑定,
当我们将dog类的对象d的地址赋给a时,C++编译器进行了类型转换,此时C++编译器认为变量a保存的就是 animal对象的地址。当在main()函数中执行a->eat()时,调用 的当然就是animal对象的eat函数。
第二个输出的是this指针的地址
第三个输出是因为父类有虚函数子类会将其重写,运行时再去确定对象的类型以及正确的调用函数,除非是显示访问不然只会访问子类成员函数
第四个和第五个对比可以看出子类大小比父类大4 因为子类又多了个data2
还有要注意纯虚函数在成员函数的形参后面写上=0,则成员函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类 中重新定义以后,派生类才能实例化出对象。
(四)对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针
class CTest
{
public:
CTest(){ iTest = 10; }
/*virtual */~CTest()
{
cout<<"this="<<this<<endl;
};
private:
int iTest;
};
int main()
{
cout << sizeof(CTest) << endl;
system("pause");
return 0;
输出结果
看监视窗口和反汇编
转到下一步时
add 一个4
验证了对于有虚函数的类,编译器都会维护一张虚表,对象的前四个字节就是指向虚表的指针
(五)虚表分析
#include<iostream>
using namespace std;
class CBase
{
public:
virtual void FunTest0(){ cout << "CBase::FunTest0()" << endl; }
virtual void FunTest1(){ cout << "CBase::FunTest1()" << endl; }
virtual void FunTest2(){ cout << "CBase::FunTest2()" << endl; }
virtual void FunTest3(){ cout << "CBase::FunTest3()" << endl; }
};
class CDerived :public CBase
{
public:
virtual void FunTest0(){ cout << "CDerived::FunTest0()" << endl; }
virtual void FunTest1(){ cout << "CDerived::FunTest1()" << endl; }
virtual void FunTest4(){ cout << "CDerived::FunTest4()" << endl; }
virtual void FunTest5(){ cout << "CDerived::FunTest5()" << endl; }
};
typedef void(*_pFunTest)();
void FunTest()
{
CBase base;
for (int iIdx = 0; iIdx < 4; ++iIdx)
{
_pFunTest pFunTest = (_pFunTest)(*((int*)*(int *)&base + iIdx));
pFunTest();
}
cout << endl;
CDerived derived;
for (int iIdx = 0; iIdx < 6; ++iIdx)
{
_pFunTest pFunTest = (_pFunTest)(*((int*)*(int *)&derived + iIdx));
pFunTest();
}
}
void TestVirtual()
{
CBase base0;
CDerived derived;
CBase& base1 = derived;
}
int main()
{
FunTest();
TestVirtual();
system("pause");
return 0;
}
输出结果是
派生类的结构
派生类虚表的形成
1先拷贝基类的虚表
2如果派生类重写了基类虚函数,则将同位置基类虚函数修改
3最后是派生类新定义的虚函数
多重继承时
- C++多态和虚表浅析
- 多态浅析(C++)
- 浅析char*和char c[]
- C语言中结构和链表浅析
- B/S 和 C/S 架构浅析
- 浅析objective-c中的strong和weak
- 浅析objective-c中的strong和weak
- 对比C/C++,浅析Java里的指针和引用
- C++ 静态多态和动态多态 浅析
- C++浅析——虚表和虚表Hook
- c/c++/c# 浅析
- 浅析c、c++,vc++
- 有关c语言中随机数函数rand()和srand()浅析
- C/C++拾遗录--关于goto和jmp语句浅析
- 浅析C语言的非局部跳转:setjmp和longjmp
- 浅析C/C++中new和malloc的区别
- 黑马程序员数组C和OC遍历异同浅析
- 浅析C 语言变量和函数命名规范
- React和React Native等学习资源整理
- 前端HTML、CSS、JS绘制三角形的方法
- 直通交换转发计算
- 【NOIP模拟题】【DP】【LIS】【中缀表达式】2016.11.15 第一题 小L的二叉树 题解
- varchar和Nvarchar区别
- C++多态和虚表浅析
- c#第五章
- iOS设计模式浅析之组合模式
- Android进阶学习笔记(一) ---- 前言
- 逻辑运算学习笔记
- 设计模式之代理模式(第二篇)
- iOS相册内ALasset类对象是nil的问题
- 多人聊天小程序
- iOS设计模式浅析之责任链设计模式