15.9 RESTRICTIONS ON OVERRIDING FUNCTIONS IN C


15.9 RESTRICTIONS ON OVERRIDING FUNCTIONS IN C++

When a function is declared virtual in a base class, it can be overridden by a function of the same signature in a derived class. The overriding function definition in the derived class must not violate certain restrictions[9]:

  1. When the values returned are of primitive types, the return type of the overriding function in a derived class must be the same as the return type of the overridden function in the base class. Consider the following example:

     class X {                                 // BASE public:     virtual float foo(double m) { return m; } }; class Y : public X {                      // DERIVED public:     double foo(double n) { return n; }    // Error }; 

    The compiler would not accept this. By using in the derived class a function of the same signature as a virtual function in the base class, you have told the compiler that you want to provide an overriding function in the derived class for the virtual function in the base class.[10] The compiler would now insist that the return types for both versions of foo() be identical. Of course, if we were to change the parameter type of the derived-class foo to, say, float, the compiler would not complain. The derived-class foo would now be considered to be a different function, not meant for overriding the base-class foo.

  2. When the returned values are pointers or references to class types, the return type of an overriding function is allowed to be a subclass of the type returned by the base-class virtual function. In the following program, note the return type of the overriding bar() in line (B) vis-à-vis the return type of the overridden bar() in line (A).

     
    //OverrideReturnRestrict.cc #include <iostream> using namespace std; class X {}; // BASE class Y : public X {}; // DERIVED class Base { // BASE public: virtual X* bar() { //(A) cout << "Base's bar invoked" << endl; return new X(); } virtual ~Base(){} }; class Derived : public Base { // DERIVED public: Y* bar(){ //(B) cout << "Derived's bar invoked" << endl; return new Y (); } ~Derived(){} }; int main() { Base* b = new Derived(); b->bar(); // program's output: Derived's bar invoked delete b; return O; }

    In main of the program above, we invoke the function bar on a base-class pointer b that is actually pointing to a derived-class object. The output of the program, shown commented out in main, reveals that Derived's bar is correctly chosen for invocation at run time. (Section 15.10 explains why the destructor in the above program was declared to be virtual for the class Base.)

  3. The access restriction on the base class virtual function plays no role in the legality of an override definition in a derived class. Obviously, if the base-class virtual function is in the public section, the derived class's override definition can be in either the private, or the protected, or the public section. But the same is true if the base-class virtual function is either private or protected. This is illustrated by the following example in which the virtual function foo(), defined in line (C), is in the private section of Base. This function is invoked by the public function bar() defined in line (D). In main(), we construct a derived-class object but assign its address to a base-class pointer in line (G). When we invoke bar() on the base-class pointer in line (H), it is the derived-class version of foo (), defined in line (F), that gets executed.

     
    //PrivateVirtual.cc #include <iostream> using namespace std; class Base { // BASE int m; virtual void foo(){cout <<"Base's foo invoked" << endl;} //(C) public: Base( int mm ) : m( mm ) {} void bar() { foo(); } //(D) virtual ~Base(){} //(E) }; class Derived : public Base { // DERIVED int n; void foo() { cout << "Derived's foo invoked" << endl; } //(F) public: Derived( int mm, int nn ) : Base( mm ), n( nn ) {} ~Derived(){} }; int main() { Base* p = new Derived( 10, 20 ); //(G) p->bar(); //output: Derived's foo invoked //(H) delete p; return o; }

    See Section 15.10 as to why the base class destructor in line (E) has been declared virtual.

  4. An overriding function in a derived class is not allowed to throw an exception that is excluded by the exception specification of the overridden function in the base class. In the following example, the virtual function foo() in line (I) in the class Base is allowed to throw an exception of type E1. The overriding foo() in Derived_1 throws an exception of type E2, a subtype of E1. Since an object of type E2 is also an object of type E1, that is permissible. However, what is shown in line (K) is not permissible. Here, the overriding foo() of Derived_2 throws an exception of type E3 that is not of type E1. So the compiler will not except the code in line (K).

     
    //OverrideExceptionRestrict.cc #include <iostream> using namespace std; class E1 {}; // BASE exception type class E2 : public E1 {}; // DERIVED exception type class E3 {}; class Base { // BASE int m; public: Base( int mm ) : m( mm ) {} virtual void foo() throw( E1 ) { //(I) cout << "Base's foo" << endl; throw E1(); } virtual ~Base() {} }; class Derived_1 : public Base { // DERIVED int n; public: Derived_1( int mm, int nn ) : Base( mm ), n( nn ) {} void foo() throw( E2 ) { //(J) cout << "Derived_1's foo" << endl; throw E2(); } ~Derived_1() {} }; class Derived_2 : public Base { // DERIVED int p; public: Derived_2( int mm, int pp ) : Base( mm ), p( pp ) {} // void foo() throw (E3) {} //ERROR //(K) ~Derived_2() {} }; int main() { Base* p = new Derived_1( 10, 20 ); try { p->foo(); //(A //(L) } catch( E1 e ) { cout << "caught E1" << endl; } delete p; return O; }

    The output of the program is

         Derived_1's foo     caught E1 

    indicating that even though we invoked foo on a base-class pointer in line (L) of main, it was the Derived_1's definition of foo that was used.

[9]There are marked differences between C++ and Java with regard to the restrictions on overriding functions. The reader might wish to compare on an item-by-item basis the contents of this section with a similarly titled section later for Java.

[10]Recall from Chapter 9 that by a function signature we mean the function name, followed by an ordered list of its parameter types.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net