当前位置: 首页 > news >正文

做网站难吗windows优化大师最新版本

做网站难吗,windows优化大师最新版本,Wordpress哪些出现403,个人做企业网站制作要多少钱注意 virtual关键字: 1、可以修饰原函数,为了完成虚函数的重写,满足多态的条件之一 2、可以菱形继承中,去完成虚继承,解决数据冗余和二义性 两个地方使用了同一个关键字,但是它们互相一点关系都没有 虚函…

注意   virtual关键字:

    1、可以修饰原函数,为了完成虚函数的重写,满足多态的条件之一

   2、可以菱形继承中,去完成虚继承,解决数据冗余和二义性

两个地方使用了同一个关键字,但是它们互相一点关系都没有

虚函数重写:

 

多态的条件:

1、虚函数的重写

2、父类对象的指针或者引用去调用虚函数

必须是父类指针或者引用

不可以是子类因为父类不可以传给子类

class Person
{
public:virtual void BuyTicket() { cout << "Person全票" << endl; }
};
class Student : public Person
{
public:virtual void BuyTicket() { cout << "Student半票" << endl; }
};
void func(Person& p1)
{p1.BuyTicket();
}
int main()
{Person p1;Student s1;func(p1);func(s1);return 0;
}

协变(是多态的一种特殊情况):

多态:

1、虚函数的重写(必须要函数名、返回值、参数要相同)

2、父类对象的指针或者引用去调用虚函数

但是协变可以返回值可以不同

但是返回值必须是基类的指针或引用和子类的指针或引用

//class A
//{
//};
//class B :public A
//{
//}
//其他类的基类和派生类也可以
//class Person
//{
//public:
//	virtual A* BuyTicket() { cout << "Person全票" << endl; return nullptr; }
//};
//class Student : public Person
//{
//public:
//	virtual B* BuyTicket() { cout << "Student半票" << endl;  return nullptr; }
//};
//void func(Person& p1)
//{
//	p1.BuyTicket();
//}
//class Person
{
public:virtual Person* BuyTicket() { cout << "Person全票" << endl; return nullptr; }
};
class Student : public Person
{
public:virtual Student* BuyTicket() { cout << "Student半票" << endl;  return nullptr;}
};
void func(Person& p1)
{p1.BuyTicket();
}
int main()
{Person p1;Student s1;func(p1);func(s1);return 0;
}

析构函数:

面试题:析构函数需不需要加vitrual?

class Person
{
public:~Person() { cout << "~Person()" << endl; }};
class Student : public Person
{
public:~Student() { cout << "~Student()" << endl;}
};int main()
{Person* p1= new Student;delete p1;return 0;
}

这种情况下父类的指针指向了new Student 但是使用完会造成内存泄漏,父类的指针只会调用父类的析构函数去清理该指向部分的空间,但是我们需要清理子类的空间就要调用子类的析构函数,所以需要加virtual 构成虚函数的重写,让父类的指针调用构成多态,就可以调用子类的析构函数。

 

看下一道面试题:

在做面试题之前先看下面代码

在继承关系中,

如何理解上述话呢?

看下面代码

在满足多态的条件下,虚函数的继承是继承了接口,所以缺省值继承了,但是子类要自己重写实现

所以当父类中的有虚函数,子类的就可以不用加virtual,但是不规范

答案:是B

为什么多态就要继承父类的接口?突然感悟

比喻:子类中的函数 drive(Banz* const this),父类也有(Car* const this)
  //子类这个this是接收不了父类的指针,只有父类的指针或引用才可以指向子类
   //所以这个继承接口才需要继承父类的接口----突然感悟

============下面代码=============== 

//作者:蚂蚁捉虫虫
//链接:https ://www.zhihu.com/question/517444641/answer/2390138862
//来源:知乎
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
#include <iostream>       // std::cout
class Base {public:Base() {};virtual void func_a(int a = 0) {}; //这个是虚函数,子类只继承接口,具体的实现,由子类去实现void func_b(int b) { std::cout << b + 10 << "\n"; }; //这个是实函数,其接口和实现,都会被子类继承
};class Base_A : public Base {
public:void func_a(int a=15) { std::cout << a << "\n"; };
};class Base_B : public Base {
public:void func_a(int a) { std::cout << a + 15 << "\n"; };
};int main()
{Base_A a;Base_B b;a.func_a(); //仅仅继承了基类的接口,但没有继承实现a.func_b(10); //继承了基类的接口及实现std::cout << std::endl;b.func_a(10); //仅仅继承了基类的接口,但没有继承实现b.func_b(10); //继承了基类的接口及实现return 0;
}

 

只有在满足多态的情况下,虚函数的继承才是父类的虚函数继承对于子类来说继承的是父类的接口(包括缺省值),子类函数的实现需要子类来写

上述代码只是完成了重写,并没有满足多态,所以并没有继承接口

关键字final和override

1、final修饰虚函数,表示该虚函数不能再被继承

也可以修饰class叫最终类不能被继承

override关键字:检查子类的虚函数是否完成重写

构成虚函数重写吗?

没有,认真看,但是不会报错,所以,加上override就可以自动检测检查子类的虚函数是否完成重写

重载、重写、重定义

抽象类

可以看下列代码:

//作者:蚂蚁捉虫虫
//链接:https://www.zhihu.com/question/517444641/answer/2390138862
//来源:知乎
//著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。class Base {public:Base(){};virtual void func_a(int a) = 0; //这个是纯虚函数,子类只继承接口,具体的实现,由子类去实现void func_b(int b) {std::cout << b+10 << "\n";}; //这个是实函数,其接口和实现,都会被子类继承
};class Base_A: public Base{
public:void func_a(int a){std::cout << a << "\n";};
};class Base_B: public Base{
public:void func_a(int a){std::cout << a + 15 << "\n";};
};int main ()
{Base_A a;Base_B b;a.func_a(10); //仅仅继承了基类的接口,但没有继承实现a.func_b(10); //继承了基类的接口及实现std::cout << std::endl;b.func_a(10); //仅仅继承了基类的接口,但没有继承实现b.func_b(10); //继承了基类的接口及实现return 0;
}

上述代码里,定一个基类,里面有两个成员函数,一个是虚函数,一个是实际函数;然后又定义了两个子类,Base_A和Base_B,两个子类对基类中的func_b函数有不一样的实现

纯虚函数的作用强制子类完成重写

表示抽象的类型。抽象就是在现实中没有对应的实体的

接口继承和实现继承

多态的原理:

测试我们发现b对象是8个字节,除了_b成员,还多了一个指针_vfptr放在对象对面,我们叫做虚函数指针我们叫做虚函数表指针。一个含有虚函数表的类中至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表称虚表

注意:虚函数存放在哪里? 虚表存在哪里

虚表存的是虚函数指针,不是虚函数,虚函数也是函数所以也是存在代码区,只是它的地址被存进虚函数指针中,这个指针被虚表记录着

重写:接口继承,实现重写,在原理上是覆盖将父类继承下来的vfptr的父类虚函数的地址覆盖成子类的虚函数地址

从反汇编看原理:

普通类函数:

在编译的过程中就已经确定了调用函数的地址

现在我们加上virtual虚函数

进入汇编,当形成多态时是如何调用的 

00B021E1 8B 45 08             mov         eax,dword ptr [A]  //将A指向空间地址给eax
00B021E4 8B 10                mov         edx,dword ptr [eax]  //将eax空间中的前四个字节地址给edx就是虚函数表指针
00B021E6 8B F4                mov         esi,esp//这个是维护函数栈帧的寄存器,不用管  
00B021E8 8B 4D 08             mov         ecx,dword ptr [A]  //将A指向空间地址给ecx
00B021EB 8B 42 04             mov         eax,dword ptr [edx+4]  //因为edx保存的是前四个字节空间的地址就是虚函数表指针+4就是run()的地址,将run()地址给eax,前4个是speak()的地址
00B021EE FF D0                call        eax //调用run()
00B021F0 3B F4                cmp         esi,esp 
00B021F2 E8 1A F1 FF FF       call        __RTC_CheckEsp (0B01311h) 

 多态就是有virtual函数是用虚函数表指针去存放虚函数的地址,在由虚函数表指针调用对应的函数

面试题:

虚函数存在哪里?代码段,虚函数和普通函数一样都是函数所以都是编译成指令存进代码段中

虚函数表存在哪里?

存在代码段中,不是存在栈区,因为栈区是由一个个栈帧堆建的所以每调用创建一个对象就要建立一个虚表是很消耗内存的

证明一下:

虚表存放在代码区中的代码段最合适,堆区是动态开辟的数据区分为bss区(存放未初始化的static和未初始化的全局变量)数据区存放(存放初始化的static和初始化的全局变量),所以代码段是最合适的

反向验证:

发现很接近代码区

总结:

多态的本质原理,符合多态的两个条件。那么在父类的指针或引用调用时,会到指向对象的虚表找到对应的虚函数地址,进行调用

多态(程序运行时去指向对象的虚表中找到函数地址,进行调用,所以p指向谁就调用谁的虚函数)

普通函数的调用,编译链接时确定函数的地址,运行时直接调用。类型时谁就是谁调用

动态绑定和静态绑定:

编译:就是代码和语法检查其实就是预处理、编译、汇编、链接

运行:就是将可执行文件加载到内存中进行对数据区的数据替换

静态绑定:更具调的类型就确定了调用的函数

动态绑定:运行时具体拿到类型确定程序的具体行为,就是在编译时无法确定函数的行为

运行时根据寄存器去拿到函数的地址

单继承和多继承的虚表(不是虚基表)

单继承:

void(*p)();  //函数指针

补充:

函数名就是函数的地址

那我们手动打印虚函数表

class base
{
public:virtual void func1() { cout << "base::func1()" << endl; }virtual void func2() { cout << "base::func2()" << endl; }};
class derive :public base
{
public:virtual void func1() { cout << "derive::func1()" << endl; }virtual void func3() { cout << "derive::func3()" << endl; }virtual void func4() { cout << "derive::func4()" << endl; }};
//void(*)()
typedef void(*VF_PTR)();//重命名函数指针void PrintVFTable(VF_PTR* pTable)//VF_PTR pTable[]  函数指针数组==虚函数表指针
{for (size_t i = 0; pTable[i] != 0; i++){printf("pTable[%d]=%p->", i, pTable[i]);VF_PTR f = pTable[i];//得到函数的地址==函数名f();}cout << endl;
}int main()
{base b1;derive d2;PrintVFTable((VF_PTR*)(*(int*)&b1));//取b1的地址因为要取到虚函数表指针,它在对象的前四个字节//所以转换成int*在解引用就是取空间b1的前四个字节,因为此时是int*//所以要转成VF_PTR*PrintVFTable((VF_PTR*)(*(int*)&d2));return 0;
}

多继承的虚表:

计算一下test 对象等于多少?

class base
{
public:virtual void func1() { cout << "base::func1()" << endl; }virtual void func2() { cout << "base::func2()" << endl; }int i = 0;
};
class derive
{
public:virtual void func1() { cout << "derive::func1()" << endl; }virtual void func3() { cout << "derive::func3()" << endl; }virtual void func4() { cout << "derive::func4()" << endl; }int i = 0;
};
class test:public base,public derive
{
public:virtual void func3() { cout << "test::func1()" << endl; }virtual void func2() { cout << "test::func3()" << endl; }virtual void func7() { cout << "test::func4()" << endl; }
public:int i = 0;
};//void(*)()
typedef void(*VF_PTR)();//重命名函数指针void PrintVFTable(VF_PTR* pTable)//VF_PTR pTable[]  函数指针数组==虚函数表指针
{for (size_t i = 0; pTable[i] != 0; i++){printf("pTable[%d]=%p->", i, pTable[i]);VF_PTR f = pTable[i];//得到函数的地址==函数名f();}cout << endl;
}int main()
{test i;cout << sizeof(i) << endl;return 0;
}

等于20   

编译器又没显示!!!那我们手动去看看

继承的子类和其父类的表不是同一张表,只有同一类才是用一张表哦

http://www.ds6.com.cn/news/36658.html

相关文章:

  • 垂直汽车网站做电商的优势国内新闻大事20条简短
  • 网站维护公司广州最新新闻国内大事件
  • 动漫做的游戏 迅雷下载网站有哪些推广产品的软文
  • 湖州市南浔区建设局网站如何进行网站推广
  • 做网站系统学校广东: 确保科学精准高效推进疫情
  • 新手如何建立自己网站在线咨询 1 网站宣传
  • 日本人在床上做裸身体网站啊长沙seo公司排名
  • 计算机做网站难吗软件测试培训班多少钱
  • 网站的icp 备案信息成都电脑培训班零基础
  • asp.net 4.0网站开发与项目实战(全程实录)(附光盘)自助建站系统软件
  • 展示系统 网站模板百度一下官方入口
  • 网站域名备案与解析营销模式有几种
  • 电商网站开发企业短链接在线生成器
  • 酒类产品网站设计网站首页推广
  • 室内设计效果图一套seo公司是做什么的
  • 广东网站备案进度查询软文范例大全800
  • 求做网站的搜索关键词优化服务
  • office做网站的新产品宣传推广策划方案
  • 做电商网站运营搜索引擎优化的核心本质
  • 早晨设计 做网站设计吗天津seo博客
  • 小程序localstorage汕头seo推广优化
  • 扬中新闻网站seo怎么推排名
  • 网站悬浮图标怎么做郑州seo网络推广
  • 响应式设计网站怎么做讯展网站优化推广
  • 手机直接看的网站有哪些新闻摘抄大全
  • 玉树北京网站建设想学网络营销怎么学
  • 安阳做网站的公司百度客服电话号码
  • 邢台做网站服务商设计网站大全
  • 宁波网站建设开发服务揭阳百度seo公司
  • 用css3做酷炫网站湖南网络推广服务