1、改变覆盖方法的返回类型:协变返回类型covariant returns type 一般情况下,子类覆盖父类的方法时,是不允许修改函数返回类型的。 CODE 1 class Foo { public: virtual int overload( ) {cout<<"Foo's overload()/n ";return 1;} //返回类型int virtual void overload( int i ){cout<<"Foo's overload(int "<< i <<" )/n ";} }; class Bar:public Foo { public: virtual float overload( ){cout<<"Bar's overload()/n ";return 2.22;} //返回类型float }; 编译器GCC4.6.0会提示: test.cpp:14:18: error: conflicting return type specified for ‘virtual float Bar::overload()’ test.cpp:7:19: error: overriding ‘virtual int Foo::overload()’ 但是,在C++中,只要原来的返回类型是指向类的指针或引用,新的返回类型是指向其派生类的指针或引用, 覆盖方法就可以改变返回类型。这样的类型成为协变返回类型。 (也就是说,两个返回类型必须满足is-a的关系) 2、改变覆盖方法的参数 通常,如果要修改覆盖方法的参数,就不再是覆盖了,而是你创建了一个新的方法,但是因为重名, 又无法调用父类的方法,可以使用using来进行函数签名声明。 CODE 2 #include <iostream> using namespace std; class Foo { public: virtual void overload( ) {cout<<"Foo's overload()/n ";} }; class Bar:public Foo { public: virtual void overload( int i ){cout<<"Foo's overload(int "<< i <<" )/n ";} //改变参数 }; int main( ) { Bar bar_one; bar_one.overload( ); //无法调用父类的方法 Foo* ptrFoo=&bar_one; ptrFoo->overload( ); return 0; } 结果是出现编译错误: test.cpp: In function ‘int main()’: test.cpp:19:28: error: no matching function for call to ‘Bar::overload()’ test.cpp:19:28: note: candidate is: test.cpp:13:18: note: virtual void Bar::overload(int) test.cpp:13:18: note: candidate expects 1 argument, 0 provided 如上所述,修改将要覆盖的方法参数,将会产生一个新的方法。 3、覆盖方法的特殊情况 一、在C++中,不能覆盖父类的static方法 二、不能同时使用static和virtual声明一个方法 在C++中,调用static方法时,编译器不关心对象实际是什么,而只关心对象编译时的类型。 三、覆盖方法时,其实是隐式地隐藏了该方法/同名方法的其他实现。 四、为了避免不明确的BUG,应该明确的指出或者使用关键字using来引入父类中被覆盖的方法。 五、超类方法为private或protected 记住一点,方法的访问限定符只是决定了谁可以调用该方法。 虽然,子类不能调用父类的private方法,但是这不影响子类对父类privat/protected方法的覆盖。 六、超类方法有默认参数 子类和超类都可以有不同的默认参数,但是使用的参数是和变量的类型绑定,而不是该类型的变量。 (也就是说如果超类int i=7,那么子类所有的int类型的默认参数都为7) #include <iostream> using namespace std; class Foo { public: virtual void go( int i=2 ){cout<<"Foo' go( ) with param "<<i<<endl;} }; class Bar:public Foo { public : virtual void go( int i=4 ){cout<<"Bar' go( ) with param "<<i<<endl;} }; int main( ) { Foo myFoo; Bar myBar; Foo& myFooBar=myBar; myFoo.go( ); //output 2 myBar.go( ); //output 4 myFooBar.go( ); //output 2 return 0; } 结果: Foo' go( ) with param 2 Bar' go( ) with param 4 Bar' go( ) with param 2 myFooBar.go( ); 输出2的原因是C++把默认参数与指示对象的变量类型(ex:int i=2中的int)绑在一起,而不是与对象本身(ex:i). 也就是由于这个原因,在C++中,不能继承默认参数。 注意: 覆盖有默认参数的方法时,也应该提供默认参数,而且往往要有相同的值。 七、超类方法有不同的访问级别 如果想要修改方法的访问级别,有两种方式: • 改变继承时使用的权限修饰符 • 在子类中重新定义访问修饰符,但是注意不会影响到超类方法的访问权限 下面举例讲解修改子类的访问修饰符: #include <iostream> using namespace std; class Super { public: virtual void talk( ){cout<<"Super says hi in public talk( )/n";} }; class Sub:public Super { protected: virtual void talk( ){cout<<"Sub says hi in protected talk( )/n";} }; int main( ) { Super mySuper; Sub mySub; mySuper.talk( ); //mySub.talk( ); // error: ‘virtual void Sub::talk()’ is protected Super& mSBptr=mySub; mSBptr.talk( ); //超类的指针或者引用可以访问子类的非public方法 return 0; } 结果: Super says hi in public talk( ) Sub says hi in protected talk( ) 这也证明在子类中确实覆盖了方法,并且声明为protected成功,但是,如果超类使得其为public,就不能完全保证protected访问。 所以,可知改变方法访问级别的唯一有用方式是给超类的protected方法提供约束更少的方法:在子类中有public方法提供一个 对超类protected方法的接口。 八、类继承时复制构造函数与相等操作符 如果子类中没有特殊的数据(通常是指针)需要非默认的复制构造函数或operaotr=,就不许要有非默认的复制构造函数,不管超类中是否包括非默认的复制构造函数。如果子类中省略了复制构造函数,在复制对象时依然会调用父类的复制构造函数。operator=与复制构造函数类似。 注意: 如果子类中没有指定复制构造函数或operator=,父类的功能继续有效。如果子类覆盖自己的复制构造函数和operator=,则需要明确引用父类的复制构造函数或operator=。 如: Sub& Sub::operator=( const Sub& inSub) { if( &inSub==*this ) return *this; Super::operator=( inSub ); //call parent's operator=( ) //some other operations return ( *this ); }