C++ override关键字使用详解
C++override从字面意思上,是覆盖的意思,实际上在C++中它是覆盖了一个方法并且对其重写,从而达到不同的作用。在我们C++编程过程中,最熟悉的就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法。还有一个典型应用就是在继承中也可能会在子类覆盖父类的方法。
公有继承包含两部分:一是“接口”(interface),二是"实现"(implementation)。
例如Person类的几种成员函数的继承方式:
classPerson{ public: virtualvoidEat()const=0;//1)纯虚函数 virtualvoidSay(conststd::string&msg);//2)普通虚函数 intName()const;//3)非虚函数 }; classStudent:publicPerson{...}; classTeahcer:publicPerson{...};
1.纯虚函数
纯虚函数,继承的是基类成员函数的接口,必须在派生类中重写该函数的实现:
Person*s1=newStudent; s1->Eat();//callsStudent::Eat Person*t1=newEllipse; t1->Eat();//callsTeacher::Eat
若想调用基类的Eat(),须加上类作用域操作符::
s1->Person::Eat();//callsPerson::Eat
2.普通虚函数
普通虚函数,对应在基类中定义一个缺省的实现(defaultimplementation),表示继承的是基类成员函数的接口和缺省的实现,由派生类自行选择是否重写该函数。
实际上,允许普通虚函数同时继承接口和缺省实现是危险的。如下,CarA和CarB是Car的两种类型,且二者的运行方式完全相同。
classCar{ public: virtualvoidRun(constCar&destination); }; classCarA:publicCar{...}; classCarB:publicCar{...};
这是典型的面向对象设计,两个类共享一个特性--Run,则Run可在基类中实现,并由两个派生类继承。
现增加一个新的飞机型号CarC,其飞行方式与CarA,CarB并不相同,假如不小心忘了在CarC中重写新的Fly函数
classCarC:publicCar{ ...//noflyfunctionisdeclared };
则调用CarC中的Run函数,就是调用Car::Run,但是CarC的运行方式和缺省的并不相同
Car*pa=newCarC; pa->Run(Beijing);//callsCar::Run!
这就是前面所说的,普通虚函数同时继承接口和缺省实现是危险的,最好是基类中实现缺省行为(behavior),但只有在派生类要求时才提供该缺省行为.
方法一:
一种方法是纯虚函数+缺省实现,因为是纯虚函数,所以只有接口被继承,其缺省的实现不会被继承。派生类要想使用该缺省的实现,必须显式的调用:
classCar{ public: virtualvoidRun(constRun&destination)=0; }; voidCar::Run(constAirport&destination) { //apurevirtualfunctiondefaultcodeforRunanCartothegivendestination } classCarA:publicCar{ public: virtualvoidRun(constCar&destination){Car::Run(destination);} };
这样在派生类CarC中,即使一不小心忘记重写Run函数,也不会调用Car的缺省实现
classCarC:publicCAr{ public: virtualvoidRun(constCar&destination); }; voidCarC::Run(constCar&destination) { //codeforRunaCarCCartothegivendestination }
方法二:
可以看到,上面问题的关键就在于,一不小心在派生类CarC中忘记重写Run函数,C++11中使用关键字override,可以避免这样的“一不小心”。
非虚函数:
非虚成员函数没有virtual关键字,表示派生类不但继承了接口,而且继承了一个强制实现(mandatoryimplementation),既然继承了一个强制的实现,则在派生类中,无须重新定义继承自基类的成员函数,如下:
使用指针调用Name函数,则都是调用的Person::Name()
Students1;//s1isanobjectoftypeStudent Person*p=&s1;//getpointertos1 p->Name();//callName()throughpointer Student*s=&s1;//getpointertos1 s->Name();//callName()throughpointer
如果在派生类中重新定义了继承自基类的成员函数Name呢?
classStudent:publicPerson{ public: intName()const;//hidesPerson::Name }; p->Name();//callsPerson::Name() s->Name();//callsStudent::Name()
此时,派生类中重新定义的成员函数会“隐藏”(hide)继承自基类的成员函数
这是因为非虚函数是“静态绑定”的,p被声明的是Person*类型的指针,则通过p调用的非虚函数都是基类中的,既使p指向的是派生类。
与“静态绑定”相对的是虚函数的“动态绑定”,即无论p被声明为Person*还是Student*类型,其调用的虚函数取决于p实际指向的对象类型
重写(override)
在程序中加override关键字,可以避免派生类中忘记重写虚函数的错误
下面以重写虚函数时,容易犯的四个错误为例,详细阐述之
classBase{ public: virtualvoidfun1()const; virtualvoidfun2(intx); virtualvoidfun3()&; voidfun4()const;//isnotdeclaredvirtualinBase }; classDerived:publicBase{ public: virtualvoidfun1();//declaredconstinBase,butnotinDerived. virtualvoidfun2(unsignedintx);//takesanintinBase,butanunsignedintinDerived virtualvoidfun3()&&;//islvalue-qualifiedinBase,butrvalue-qualifiedinDerived. voidfun4()const; };
在派生类中,重写(override)继承自基类成员函数的实现(implementation)时,要满足如下条件:
一虚:基类中,成员函数声明为虚拟的(virtual)
二容:基类和派生类中,成员函数的返回类型和异常规格(exceptionspecification)必须兼容
四同:基类和派生类中,成员函数名、形参类型、常量属性(constness)和引用限定符(referencequalifier)必须完全相同
如此多的限制条件,导致了虚函数重写如上述代码,极容易因为一个不小心而出错
C++11中的override关键字,可以显式的在派生类中声明,哪些成员函数需要被重写,如果没被重写,则编译器会报错。
classDerived:publicBase{ public: virtualvoidfun1()override; virtualvoidfun2(unsignedintx)override; virtualvoidfun3()&&override; virtualvoidfun4()constoverride; };
classDerived:publicBase{ public: virtualvoidfun1()constoverride;//adding"virtual"isOK,butnotnecessary virtualvoidfun2(intx)override; voidfun3()&override; voidfun4()constoverride; };
1) 公有继承
- 纯虚函数 =>继承的是:接口(interface)
- 普通虚函数 =>继承的是:接口+缺省实现(defaultimplementation)
- 非虚成员函数=>继承的是:接口+强制实现(mandatoryimplementation)
2) 不要重新定义一个继承自基类的非虚函数(neverredefineaninheritednon-virtualfunction
3) 在声明需要重写的函数后,加关键字override
这样,即使不小心漏写了虚函数重写的某个苛刻条件,也可以通过编译器的报错,快速改正错误。
在使用中需要注意以下几点:
(1).覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
(2).覆盖的方法的返回值必须和被覆盖的方法的返回一致;
(3).覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
(4).被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。