网站开发的图片,做牛仔的时尚网站,网站地址查询域名,排名好的锦州网站建设文章目录 概念多态如何实现的指向谁调谁#xff1f;例子分析 含有虚函数类的大小是多少#xff1f;虚函数地址虚表地址多继承的子类的大小怎么计算#xff1f;练习题虚函数和虚继承 概念 
优先使用组合、而不是继承; 继承会破坏父类的封装、因为子类也可以调用到父类的函数;… 文章目录 概念多态如何实现的指向谁调谁例子分析 含有虚函数类的大小是多少虚函数地址虚表地址多继承的子类的大小怎么计算练习题虚函数和虚继承   概念 
优先使用组合、而不是继承; 继承会破坏父类的封装、因为子类也可以调用到父类的函数; 子类必须实现父类的纯虚函数; 内联函数没有地址他是直接在定义的地方展开。所以不能是虚函数、虚函数是要有地址的 虚函数不能是static函数因为没有this指针、等于说是全局的虚表要的是对象里面的 普通函数继承是实现继承就是不去重写父类的函数体直接用的虚函数的继承目的就是要重写这个函数体自改成自己想要的 虚函数在编译阶段就生成了并且存在代码段 
析构函数最好写成多态。多态就是指向谁调谁这样子类就可以调用自己的析构函数 构造函数不能写成多态因为对象中的虚表指针实在初始化列表时开始初始化的 
多态如何实现的指向谁调谁 
虚函数的地址存在虚函数表中通过虚函数表来调用虚函数 同类型的对象共用一个虚表 子类不重写父类的虚函数也是和父类共用同一张虚表重写就不会共用虚表 
例子 
#define _CRT_SECURE_NO_WARNINGS#include iostream
using namespace std;class person
{
public:virtual void BuyTicket(){cout  成人票  endl;}
};class student : public person
{
public:virtual void BuyTicket(){cout  学生票  endl;}
};void buy(person b)
{b.BuyTicket();
}int main()
{student s;person p;buy(s);buy(p);return 0;
}分析 
1、子类继承后虚函数表是一样的重写后就把虚函数表copy出来换一个地址把新的内容覆盖了  2、调用时传过去的参数累心是谁的就指向谁的虚函数表s是student类型那就是用student里面的函数  2、子类虚函数不重写会怎样 如果不重写那父级和子级虚函数表的地址是相同的  
含有虚函数类的大小是多少 
普通函数不占空间虚函数有个指针、所以站指针的大小空间32位的4字节、64位的8字节 在类里面虚函数占一个指针的大小不管有多少个虚函数就只占一个指针的大小对象里面只是存了一个指针指向这个虚表 虚表的大小才要看有几个虚函数 多继承的子类继承了几个父类就有几个父类的指针大小 如下就一个虚函数64位  计算方法和结构体其实一样内存对齐 加了一个int变量理应84  12但是最小对齐数为8类的大小是其成员最小对齐数的整数倍所以是16  
虚函数地址 
#define _CRT_SECURE_NO_WARNINGS#include iostream
using namespace std;class person
{
public:virtual void BuyTicket(){cout  成人票  endl;}
private:int a;
};class student : public person
{
public:virtual void BuyTicket(){cout  学生票  endl;}
};void buy(person b)
{b.BuyTicket();
}class base
{
public:virtual void func1(){cout  base::func1  endl;}
private:int _b  1;
};void func()
{person b1;printf(vftptr:%p\n, *(int*)  b1);int i  0;int* p1  i;int* p2  new int;const char* p3  sad;printf(栈变量%p\n, p1);printf(堆变量%p\n, p2);printf(代码段常量%p\n, p3);printf(代码段函数地址%p\n, base::func1);}int main()
{student s;person p;buy(s);buy(p);int size  sizeof(s);cout  size  endl;func();return 0;
}问题虚函数存在哪?虚函数表存在哪? 答虚函数和普通函数都存在代码段、虚函数表也存在代码段  普通的函数、函数名就是地址首地址 成员函数的地址要加取地址符号以及要指定属于哪个类也就是这样 类名::函数名 
虚表地址 
class Base
{
public:virtual void fuc1(){cout  fuc1  endl;}virtual void fuc2(){cout  fuc2  endl;}
private:int a;
};class derive : public Base
{
public:virtual void fuc1(){ cout  fuc1111  endl; }virtual void fuc3(){cout  fuc3  endl;}
};typedef void(*VF_PTR)();//函数指针类型怎么写的
void PrintVFTable(VF_PTR* pTable)
{for(size_t i  0; pTable[i] ! 0; i){printf(vfTable[%d]:%p-, i, pTable[i]);VF_PTR f  pTable[i];f();//运行函数}cout  endl;
}int main()
{//student s;//person p;//buy(s);//buy(p);//int size  sizeof(s);//cout  size  endl;//func();derive a;Base b;PrintVFTable((VF_PTR*)(*(int*)b));PrintVFTable((VF_PTR*)(*(int*)a));return 0;
}父类上面两行、子类为下面三行 子类fuc1重写过了虚表地址就变了没写fuc2但是继承了fuc2的虚表地址  强转两次int*是要取前4个字符(VF_PTR*)强转函数指针  函数指针怎么写 void(*)() type void(*a)(),这个a就代表函数指针了 
多继承的子类的大小怎么计算 
继承几个父类就含有几个指针 
class Base1
{
public:virtual void fuc1() { cout  b1:fuc1  endl; }virtual void fuc2() { cout  b1:fuc2  endl; }
private:int a1;
};class Base2
{
public:virtual void fuc1() { cout  b2:fuc1  endl; }virtual void fuc2() { cout  b2:fuc2  endl; }
private:int a2;
};class derive2 : public Base1, public Base2
{virtual void fuc1() { cout  b2:fuc1  endl; }virtual void fuc3() { cout  b2:fuc2  endl; }private:int a3;
};int main()
{//student s;//person p;//buy(s);//buy(p);//int size  sizeof(s);//cout  size  endl;//func();//derive a;//Base b;//PrintVFTable((VF_PTR*)(*(int*)b));//PrintVFTable((VF_PTR*)(*(int*)a));cout  sizeof(derive2)  endl;derive2 a1;PrintVFTable((VF_PTR*)(*(int*)a1));PrintVFTable((VF_PTR*)(*(int*)((char*)a1  sizeof(Base1))));return 0;
}继承两个父类两个指针的大小 不同父类的虚表继承后不会共用 derive2成员函数fuc3的虚表和Base1的虚表共用了随机共用一个父类的虚表 
练习题 
1、  2、 类中就只有变量、按顺序往下排 p3是指向整块区域所以首地址也是b1存的首地址 p1也是b1存的首地址 p2是b2的首地址 所以选c  
虚函数和虚继承 是两个完全不同的概念 用的都是virtual 
虚继承  菱形继承时B、C继承AD继承BC  如上图使用虚继承就会把公共的变量_a放到一个地址上 如果不用虚继承的话就会有三个_a的地址造成数据的冗余性 二义性也是不用虚继承造成的就是再回去找_a时有很多_a;