2024年9月派生类是否继承基类的虚函数?C++构造,析构函数可以是虚函数吗,是否可以被继承

 更新时间:2024-09-21 08:59:06

  ⑴派生类是否继承基类的虚函数?C++构造,析构函数可以是虚函数吗,是否可以被继承

  ⑵派生类是否继承基类的虚函数

  ⑶如果派生类又重新实现了这个虚函数,那么,这叫“覆盖”如果不重新实现,才是“继承”.成员函数的重载、覆盖与隐藏成员函数的重载、覆盖(override与隐藏很容易混淆,C++程序员必须要搞清楚概念,否则错误将防不胜防。..重载与覆盖成员函数被重载的特征:(相同的范围(在同一个类中;(函数名字相同;(参数不同;(virtual关键字可有可无。覆盖是指派生类函数覆盖基类函数,特征是:(不同的范围(分别位于派生类与基类;(函数名字相同;(参数相同;(基类函数必须有virtual关键字。示例--中,函数Base::f(int)与Base::f(float)相互重载,而Base::g(void)被Derived::g(void)覆盖。#include《iostream.h》classBase{public:voidf(intx){cout《《“Base::f(int)“《《x《《endl;}voidf(floatx){cout《《“Base::f(float)“《《x《《endl;}virtualvoidg(void){cout《《“Base::g(void)“《《endl;}};classDerived:publicBase{public:virtualvoidg(void){cout《《“Derived::g(void)“《《endl;}};voidmain(void){Derivedd;Base*pb=&dpb-》f();//Base::f(int)pb-》f(.f);//Base::f(float).pb-》g();//Derived::g(void)}示例--成员函数的重载和覆盖..令人迷惑的隐藏规则本来仅仅区别重载与覆盖并不算困难,但是C++的隐藏规则使问题复杂性陡然增加。这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:(如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆。(如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆。示例程序--(a中:(函数Derived::f(float)覆盖了Base::f(float)。(函数Derived::g(int)隐藏了Base::g(float),而不是重载。(函数Derived::h(float)隐藏了Base::h(float),而不是覆盖。#include《iostream.h》classBase{public:virtualvoidf(floatx){cout《《“Base::f(float)“《《x《《endl;}voidg(floatx){cout《《“Base::g(float)“《《x《《endl;}voidh(floatx){cout《《“Base::h(float)“《《x《《endl;}};classDerived:publicBase{public:virtualvoidf(floatx){cout《《“Derived::f(float)“《《x《《endl;}voidg(intx){cout《《“Derived::g(int)“《《x《《endl;}voidh(floatx){cout《《“Derived::h(float)“《《x《《endl;}};示例--(a成员函数的重载、覆盖和隐藏据作者考察,很多C++程序员没有意识到有“隐藏”这回事。由于认识不够深刻,“隐藏”的发生可谓神出鬼没,常常产生令人迷惑的结果。示例--(b中,bp和dp指向同一地址,按理说运行结果应该是相同的,可事实并非这样。voidmain(void){Derivedd;Base*pb=&dDerived*pd=&d//Good:behaviordependssolelyontypeoftheobjectpb-》f(.f);//Derived::f(float).pd-》f(.f);//Derived::f(float).//Bad:behaviordependsontypeofthepointerpb-》g(.f);//Base::g(float).pd-》g(.f);//Derived::g(int)(surprise!)//Bad:behaviordependsontypeofthepointerpb-》h(.f);//Base::h(float).(surprise!)pd-》h(.f);//Derived::h(float).}示例--(b重载、覆盖和隐藏的比较..摆脱隐藏隐藏规则引起了不少麻烦。示例--程序中,语句pd-》f()的本意是想调用函数Base::f(int),但是Base::f(int)不幸被Derived::f(char*)隐藏了。由于数字不能被隐式地转化为字符串,所以在编译时出错。classBase{public:voidf(intx);};classDerived:publicBase{public:voidf(char*str);};voidTest(void){Derived*pd=newDerived;pd-》f();//error}示例--由于隐藏而导致错误从示例--看来,隐藏规则似乎很愚蠢。但是隐藏规则至少有两个存在的理由:?写语句pd-》f()的人可能真的想调用Derived::f(char*)函数,只是他误将参数写错了。有了隐藏规则,编译器就可以明确指出错误,这未必不是好事。否则,编译器会静悄悄地将错就错,程序员将很难发现这个错误,流下祸根。?假如类Derived有多个基类(多重继承,有时搞不清楚哪些基类定义了函数f。如果没有隐藏规则,那么pd-》f()可能会调用一个出乎意料的基类函数f。尽管隐藏规则看起来不怎么有道理,但它的确能消灭这些意外。示例--中,如果语句pd-》f()一定要调用函数Base::f(int),那么将类Derived修改为如下即可。classDerived:publicBase{public:voidf(char*str);voidf(intx){Base::f(x);}};

  ⑷C++构造,析构函数可以是虚函数吗,是否可以被继承

  ⑸构造函数不能为虚函数,析构函数则可以,在C++中函数没有继承一说,继承指的是类继承,派生类的构造函数执行时需先构造基类的构造函数,而析构函数的执行顺序则刚好相反。

  ⑹C++中,子类会继承父类的虚函数表!对于父类的析构函数(虚函数也会继承吗

  ⑺上面的问题,楼上已经回答啦!我来说说这个意义吧!如果子类的的虚函数表中有父类的析构函数,看起来这个父类析构函数的存在似乎没有任何意义!!类是否需要一个虚析构函数?如果一个类,有子类那么这个类就应该需要一个虚析构函数。虚函数也只有在继承的时候才会有用的。说个例子:classA{stringt;};classB:A{strings;};intmain(){A*bp=newB;//这句话是没有问题的吧!deletebp;//但是这句话就会报错啦、除非你在A里面加上虚析构函数。return;}这个就是他的意义吧!这个例子不是我原创的,来自《c++沉思录》

  ⑻虚函数的地址是存在虚函数表中的,可以强制转换一个有虚表的类(里面有virtual函数)为DWORD_PTR来获得虚函数表的地址,通过此地址就可以找到调用此类的所有虚函数。给你举个例子:structSuper{intdata;virtualintadd(inti){returni;};virtualstringtoString()=;};classSub:publicSuper{public:stringtoString(){returnstring(“Subclass“);}};用下面的代码来访问虚函数表:DWORD_PTRpvTable;pvTable=(DWORD_PTR)?();((Sub*)pvTable)-》toString();这里第二条语句是通过一个指针来存放虚函数表的地址,第三条语句是通过虚函数表和virtual函数的名称来调用虚函数。虚拟继承是在多重继承中使用的,为了解决该类的急剧膨胀的问题(回头看看“深入理解C++对象模型“).A类有虚函数FunA.B类和C类都继承自A类,那么B类和C类都有虚化数指针,指向虚函数表.如果一个类D继承自B,C,那么就会有两个指针,分别指向B和C的虚函数表.这样是很不好的.因为BC都是继承自A,理论上只要一个指针就行.于是虚拟继承就出来了.可以参考一下STL里面Stream那一部分的设计,就是虚拟继承.(貌似)

  ⑼c++中父类中虚函数要全部继承吗

  ⑽全部继承是什么意思??继承了这个类,这个类的方法当然全部有的?你是不是想问是不是应该子类全部实现它的虚函数??不用的!只要不是纯虚函数就可以不用实现,直接用父类的就可以的

  ⑾C++中虚函数的作用是什么它应该怎么用呢

  ⑿C++中虚函数的作用:

  ⒀为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。

  ⒁在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。

  ⒂为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtualReturnTypeFunction()=;,则编译器要求在派生类中必须予以重写以实现多态性。

  ⒃同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。

  ⒄C++中虚函数的用法:

  ⒅比如你有个游戏,游戏里有个虚基类叫「怪物」,有纯虚函数「攻击」。然后派生出了三个子类「狼」「蜘蛛」「蟒蛇」,都实现了自己不同的「攻击」函数,比如狼是咬人,蜘蛛是吐丝,蟒蛇把你缠起来。

  ⒆然后出现好多怪物的时候就可以定义一个?虚基类指针数组,把各种怪物的指针给它,然后迭代循环的时候直接monster-》attack()攻击玩家就行了,大概见下图:

  ⒇使用虚函数的注意事项:

  ⒈包含虚函数的类指针列表会增大。

  ⒉析构函数的作用是在对象撤销之前做必要的“清理现场”的工作。当派生类的对象从内存中撤销的时候,会先先调用派生类的析构函数然后再调用基类的析构函数。

  ⒊当我们new一个临时对象时,若基类中包含析构函数,并且定义了一个指向该基类的指针变量。

  ⒋构造函数不能声明为虚函数

  ⒌构造函数不能声明为虚函数。如果声明为虚函数,编译器会自动报出。

  ⒍不在析构或者构造过程中调用虚函数

  ⒎在析构函数或者是构造函数中,我们绝对不能调用虚函数。即使,我们在构造函数或者析构函数中调用虚函数,也不会下降至派生类中调用函数。

  ⒏能不能解释一下C++中虚继承的工作原理

  ⒐首先,ClassA包含一个虚函数,所以sizeof(A)的结果是sizeof(虚表)的大小,也就是字节。ClassB继承ClassA,所以ClassB一定是包含ClassA,只是包含的方式不同。普通继承,ClassA的虚表会和ClassB的虚表合并,所以ClassB中还是保留一个虚表就可以了。此时sizeof(B)的结果就是.虚继承,ClassA和ClassB的关系就会微妙很多。由于C++支持多继承,所以某些情况下会出现下图中的继承关系。这种水晶继承会导致ClassD中包含两份ClassA的对象。此时就会出现访问歧义的情况。虚继承就可以避免上面的情况。ClassA的数据会被放到虚表中。ClassD会识别到来自ClassB和C的虚表,然后将两者合并。所以回到本件问题,问题中的ClassB就会包含一份虚函数表指针,一份虚类指针。也就是sizeof(B)的结果是。

  ⒑C++sizeof虚函数和虚继承

  ⒒虚继承是和多继承相关的,你这里没有多继承,virtual没有意义!除非B的派生类的派生类采用多继承,并且继承了两个以上的B的派生类或子孙类,虚继承没有什么用处;虚继承,是为了消除继承体系中的的钻石菱形,而定制的,没有别的作用。不管有没有虚继承,只要有虚函数,就会有虚函数表,就会在对象里安排一个和虚函数表相关的vptr这是C++面向对象特性的实现方法,具体安排和编译器有关,也和继承体系的复杂程度有关。由于A类并无虚函数,虚继承的必要性不大。虚继承只和对象布局相关,而对象布局不同编译器的实现是不同的。A是个空类,本身并无虚函数,单独定义一个对象,C++会为他分配至少一个字节继承后由于B类有虚函数,指向虚函数表的VPTR要占一个指针长度的空间(你这里个字节所以继承A的B类不必再为基类A的对象再分配任何字节了。你这里只有一个虚函数(无论多少虚函数,只有继承体系比较复杂---具体就是指多继承,才会有多个虚函数表,否则只有一个虚函数表,VPTR自然只有一个了,多继承的基类对象安排比较复杂,基类对象,以及虚函数表可能不止一个,单一继承,只会逐渐扩展继承体系的结构,所有继承层次对象的都是一个对象布局逐层扩展而已,虚函数表都是派生类的虚函数表,只是逐渐扩大而已,VPTR只需一个就够了所以B对象只有一个VPTR而已字节足够了,单独定义的A对象需要个或个字节;B对象里面的A对象不需要任何字节(空类B对象无数据成员,只需要一个指针就够了,所以是个字节classA{Ta,b,c;};classAB:virtualpublicA{Tx;};classAC:virtualpublicA{Ty;};classB:publicAB,publicAC{};这里B对象只继承一个A对象;所有A类的成员(成员函数和成员变量,都可以直接使用,不会造成二义性。

您可能感兴趣的文章:

相关文章