You are here:

C++/How to prevent a method in superclass to be overridden/overloaded in subclasses

Advertisement


Question
Hi there,
I am trying to implement Template method pattern in C++.

I want to have a method in superclass as a framework, which implements a algorithm and not to be changed/overridden/overloaded in subclasses to change the order or algorithm. (like functionality of final in java).
Here is my code snippet, for eg;
++++++++++++++++++++++++++++++++++++++++
class CoffeineBeverage
{
public:
void PrepareRecipe() //template method
{      
 boilWater();
 brew();   
 addCondiments();
}

 void boilWater() {cout<<"boil water"<<endl;}
 void brew()=0;
 void addCondiments()=0;
};

class Tea:public CoffeineBeverage
{
public:
void brew() {cout<<"brew tea"<<endl;}
void addCondiments() {cout<<"add lemon"<<endl;}
};


class Coffee:public CoffeineBeverage
{
public:
void brew() {cout<<"brew coffee"<<endl;}
void addCondiments() {cout<<"add milk & sugar"<<endl;}
};


void main()
{
Tea t = new Tea();
t.PrepareRecipe();
}
++++++++++++++++++++++++++++++++++++++++
I can't make the method as private as I want to use the same in main.
Also I don't think const will work for methods, its purpose is different.
I did some analysis, and think there is no language support in C++.
Even I saw the answer related to similar query, Cheshire Cat or Opaque pointer. But could not relate how this will solve my problem.
(http://en.allexperts.com/q/C-1040/prevent-overriding-functions-derived.htm).

Please let me know how I can solve this problem.

Answer
I notice this is a duplicate of a question posted to me under a different name. The only difference appears to be that the last one posted has the line:

   "Hi there,"

added. So I am not sure what is going on here. Are you the same person using two aliases, or two different people wanting to know the exact same thing with the exact same example code and question wording?

Either way something odd is going on. If you are one person who wanted the answer immediately you posted the question then maybe try posting it to people in times zones where it is _not_ Saturday night at the time!

I am answering both these questions as soon as I picked up my email Sunday afternoon.

In keeping with the spirit of these questions, my answer will be posted verbatim to both questions.

Anyhow onto the question.

The first thing to say is that you are correct in that there is no direct support in ISO standard C++ for the final concept of other languages such as Java or C# (where it is spelt 'sealed'). Quite possibly this is because C++ came first and such niceties were either not apparent or not thought to be important at the time but these newer languages have had the luxury of being able to notice these irritations in C++ and design in solutions to them.

On the other hand some C++ implementations allow the use of such facilities as compiler specific extensions. For example I believe that recent editions of Microsoft Visual C++ allow the use of sealed along with override and abstract in both managed (where they use the C++/CLI language specification anyway) and native C++ code - unless presumably you turn off all Microsoft C++ language extensions.

The second point to note is that you seem to be referring to polymorphic behaviour but I see no virtual functions that are the C++ mechanism to support this in your example. Hence the non-virtual member functions in the base class:

   class CoffeineBeverage
   {
   public:

   // ...
     
     void boilWater() {cout<<"boil water"<<endl;}
     void brew()=0;
     void addCondiments()=0;
   };

are always the ones that will be called from its PrepareRecipe member function. Additionally, as you tried to make two of these functions pure virtual but did not specify that they were virtual it is illegal C++. I would have expected you to have written something more like:

   class CoffeineBeverage
   {
   public:
       void PrepareRecipe() //template method
       {       
         boilWater();
         brew();   
         addCondiments();
       }

       virtual void boilWater() {cout<<"boil water"<<endl;}
       virtual void brew()=0;
       virtual void addCondiments()=0;

       // if we have a polymorphic base then we should have a virtual destructor
       virtual ~CoffeineBeverage() {}
   };

I presume this is a slip of the keyboard on your part and that as you were trying to make some of these pure virtual you do appreciate the correct way to do things - but note the addition of a virtual destructor. You  *must* in C++ always explicitly request that a destructor be virtual if you require one. If a base class has virtual member functions it should have a virtual destructor to ensure correct destruction of sub class objects destroyed through references or pointers to the base class.

The other thing I should note in case you were not aware of it, as this is also a different from Java. In C++ placing a member function inside a class definition as we do here marks that function as inline.

This is generally at odds with virtual in all but the most trivial of cases, so the compiler will almost certainly ignore the request and make these functions out of line - that is they must exist as real functions at runtime to be called via the virtual function despatch mechanism.

Of course making such function definitions out of line in example code makes the code longer so I am not sure if you only made the (virtual) member function definitions inline for brevity (as I do below).

OK so assuming you really meant something like the above then my next problem with your question is which member function(s) are you talking about, which one(s) would you want to be final?

I think, from what you say that it may be the template member function PrepareRecipe as this implements the 'algorithm'. You can prevent the function being overridden by leaving it as it is and _not_ declaring it virtual. You cannot however prevent sub-classes re-using the name of any member in a base class - and thus hiding the name.

This is in fact is what is occurring in your example (if you remove the =0 pure virtual function specifiers) as you declare no member functions virtual all the functions are non-virtual so those in sub-classes with the same name as those in the base class hide those in the base class.

So you ask what is the difference between name hiding/overloading and virtual member overriding?

Firstly any member in a sub-class with the same name as one in a base class hides that name in the base class. This is because the compiler searches for matching member names starting with those in sub-classes and works up the inheritance graph until it finds one or more matches or runs out of bases. As soon as it finds _any_ matching name(s) it stops then evaluates the names it found in the context so far to see if any fit the circumstances. If exactly one case fits better than any others then the selected item is used otherwise an error is generated. This can lead to eyebrow raising errors and even more unexpected runtime behaviour if an inappropriate match is found.

To show this consider this simple example:

   class B
   {
   public:
     void member() {}
   };

   class D : public B
   {
   public:
   };

   int main()
   {
     D d;
     d.member();
   }

The above should compile. The compiler starts searching for a member function match in D for the expression:

   d.member()

and, finding none in D looks in B where it finds the base B::member() function - all is well.

Now if we change D like so:

   class D : public B
   {
   public:
       int member;
   };

Oh dear. Now the compiler searches for something called member in D and finds the int data member and stops. It compares the attributes of what this names to what it wants and spits out an error as obviously this is not a function and cannot be used like one.

Compiling with Microsoft Visual C++ 2008 I received the following:

   error C2064: term does not evaluate to a function taking 0 arguments

Here is another more worrying example:

   #include <iostream>

   class B
   {
   public:
       double calc(double v) { return v<1.0 && v >-1.0 ? v : 1/v;   }
   };

   class D : public B
   {
   public:
       int calc(int v) { return v*v;  };
   };
   
   int main()
   {
       D d;
       double value(12.5);
       std::cout << "Result calculated from " << value
         << " is " << d.calc(value) << std::endl;
   }

In this case you might think that because I pass a double to d.calc() that obviously B::calc will be called and the result will be 1/12.5. In fact because D has a member called calc as well the compiler only considers D::calc and because it is a function taking one argument and returning a value and because a double can be converted to an int in this case the code compiles and when run produces the result:

   Result calculated using D from 12.5 is 144

Not what is expected at all.

However most compilers in this case will give a warning because converting a double to an int can loose data. MSVC++ 2008 gave the warning:

   warning C4244: 'argument' : conversion from 'double' to 'int', possible loss of data

However this may not always be the case - for example if we reversed the situation above and B::calc took an int and D::calc a double and we passed d.calc an int value then most likely no warning would be produced.

We can get around this problem if the hidden names in question are used only for member functions with different numbers and/or types of parameters by explicitly bringing the names from base classes into a sub-class, one of the uses of the C++ 'using' keyword. We can do so to D:

   class D : public B
   {
   public:
   
       using B::calc;  // bring members of B named calc into D

       int calc(int v) { return v*v;  };
   };

Now if we rebuilt and run the program again we find we get:

   Result calculated from 12.5 is 0.08

This is because B::calc was added to the names of members in D so the compiler found both versions (overloads) of calc when looking for a match in D and as the B::calc function was a better (exact) match it was selected.

The final aspect of base member hiding to look at is the case where the names are functions that have the same signature as those the base - how does this differ from overriding virtual functions?

Modifying the above example so that D::calc takes and returns a double like B::calc but still returns the square rather than the inverse (or v if small), and adding more test cases in main we get:

   #include <iostream>

   class B
   {
   public:
       double calc(double v) { return v<1.0 && v >-1.0 ? v : 1/v;   }
   };

   class D : public B
   {
   public:
       double calc(double v) { return v*v; };
   };
   
   int main()
   {
       B b;
       D d;
       double value(12.5);
       std::cout << "Result calculated using B from " << value
         << " is " << b.calc(value) << std::endl;
       std::cout << "Result calculated using D from " << value
         << " is " << d.calc(value) << std::endl;
       B * pB(&b);
       std::cout << "Result calculated using B* pointing to B* from " << value
         << " is " << pB->calc(value) << std::endl;
       pB = &d;
       std::cout << "Result calculated using B* pointing to D* from " << value
         << " is " << pB->calc(value) << std::endl;
       B & rBB(b);
       std::cout << "Result calculated using B& referencing a B from " << value
         << " is " << rBB.calc(value) << std::endl;
       B & rBD(d);
       std::cout << "Result calculated using B& referencing a D from " << value
         << " is " << rBD.calc(value) << std::endl;
   }

Here I have a B object and a D object and call calc on each object directly and through a pointer to B and a reference to B. This shows up another difference between Java/C# etc. and C++ - class type objects are not reference types. You have to ask for a reference either as a C/C++ style pointer type or as a C++ reference type.

Building and executing the above should produce the following output:

   Result calculated using B from 12.5 is 0.08
   Result calculated using D from 12.5 is 156.25
   Result calculated using B* pointing to B* from 12.5 is 0.08
   Result calculated using B* pointing to D* from 12.5 is 0.08
   Result calculated using B& referencing a B from 12.5 is 0.08
   Result calculated using B& referencing a D from 12.5 is 0.08

As you can see, as the member functions in question are _not_ virtual and D:calc does _not_ override B::calc there is no polymorphic behaviour. As soon as we have anything the looks like a B - be it a B, pointer to B or reference to B, B::calc is always the one that is called.

To make this a bit more like your example we can add another function to B called doCalc that calls calc. We then call the doCalc member function instead of calc in main:

   #include <iostream>

   class B
   {
   public:
       double doCalc( double v ) { return calc(v); }

       double calc(double v) { return v<1.0 && v >-1.0 ? v : 1/v;   }
   };

   class D : public B
   {
   public:
       double calc(double v) { return v*v; };
   };
   
   int main()
   {
       B b;
       D d;
       double value(12.5);
       std::cout << "Result from doCalc using B from " << value
         << " is " << b.doCalc(value) << std::endl;
       std::cout << "Result from doCalc using D from " << value
         << " is " << d.doCalc(value) << std::endl;
       B * pB(&b);
       std::cout << "Result from doCalc using B* pointing to B* from " << value
         << " is " << pB->doCalc(value) << std::endl;
       pB = &d;
       std::cout << "Result from doCalc using B* pointing to D* from " << value
         << " is " << pB->doCalc(value) << std::endl;
       B & rBB(b);
       std::cout << "Result from doCalc using B& referencing a B from " << value
         << " is " << rBB.doCalc(value) << std::endl;
       B & rBD(d);
       std::cout << "Result from doCalc using B& referencing a D from " << value
         << " is " << rBD.doCalc(value) << std::endl;
   }

This produces the following:

   Result from doCalc using B from 12.5 is 0.08
   Result from doCalc using D from 12.5 is 0.08
   Result from doCalc using B* pointing to B* from 12.5 is 0.08
   Result from doCalc using B* pointing to D* from 12.5 is 0.08
   Result from doCalc using B& referencing a B from 12.5 is 0.08
   Result from doCalc using B& referencing a D from 12.5 is 0.08

As you can see, as no polymorphism is involved, in this case B::calc is always called by doCalc. This is because calc is now always called from the B naming context (the B namespace). This would be similar to say having a non-virtual boilWater function called from (a non-virtual) CoffeineBeverage::PrepareRecipe. All specific caffeine beverage types will use the same boilWater behaviour - unless of course they re-declare/define a PrepareRecipe member function, which would only work when calling _directly_ on an object, pointer or reference to the sub-class type, which would be unlikely in most cases involving a framework.

Now, reverting to B without doCalc, if we make calc virtual by changing B thus:

   class B
   {
   public:
       virtual double calc(double v) { return v<1.0 && v >-1.0 ? v : 1/v;   }

       virtual ~B() {}
   };

Note that again I have added a virtual destructor. Although not needed in the current cases as no resources are held by objects of B and D who can say what some future sub-class of either of them may look like?

Re-running the program should give:

   Result calculated using B from 12.5 is 0.08
   Result calculated using D from 12.5 is 156.25
   Result calculated using B* pointing to B* from 12.5 is 0.08
   Result calculated using B* pointing to D* from 12.5 is 156.25
   Result calculated using B& referencing a B from 12.5 is 0.08
   Result calculated using B& referencing a D from 12.5 is 156.25

Now we get the expected polymorphic behaviour.

So to sum up:

- you must explicitly declare a function virtual if you want polymorphic behaviour, at least in the base class.
- you can prevent a sub-class overriding a base class member function by not making the function virtual
 (this of course presumes that the base class is the one that has control over whether the member function
  is virtual or not, and is not itself a base of some other class that has already declared the function
  virtual)
- base classes (having virtual member functions) should declare (and define somewhere) a virtual destructor
- you cannot prevent a sub-class from (re-)using member names already used in base classes, hiding those names.
- base class names can be brought into a sub-class naming context (namespace) by the use of using.
 This will not work if some uses of the name are data members as only function names may be overloaded.
- Re-declaring (and defining) an existing base class non-virtual function in a sub-class is a different
 situation to overriding a virtual base class member function in a sub-class.
- if a base class member function that is _not_ virtual is called from another base class member function
 then that function will always be the one called. No clashing names in sub-classes will be considered
 because it is called in the naming context (namespace) of the base class.

I am still not sure from your question if I have fully grasped the exact circumstances you are asking about partly from the typos in your example and partly because you did not clearly associate the code in your example to the situation you describe in the question text. Adding comments to your example code to show where you would have used final would have been a great help for example.

Hence if I have not grasped correctly what it is you are attempting to do please post a follow up question - preferably with more exact details for my poor brain!  

C++

All Answers


Answers by Expert:


Ask Experts

Volunteer


Ralph McArdell

Expertise

I am a software developer with more than 15 years C++ experience and over 25 years experience developing a wide variety of applications for Windows NT/2000/XP, UNIX, Linux and other platforms. I can help with basic to advanced C++, C (although I do not write just-C much if at all these days so maybe ask in the C section about purely C matters), software development and many platform specific and system development problems.

Experience

My career started in the mid 1980s working as a batch process operator for the now defunct Inner London Education Authority, working on Prime mini computers. I then moved into the role of Programmer / Analyst, also on the Primes, then into technical support and finally into the micro computing section, using a variety of 16 and 8 bit machines. Following the demise of the ILEA I worked for a small company, now gone, called Hodos. I worked on a part task train simulator using C and the Intel DVI (Digital Video Interactive) - the hardware based predecessor to Indeo. Other projects included a CGI based train simulator (different goals to the first), and various other projects in C and Visual Basic (er, version 1 that is). When Hodos went into receivership I went freelance and finally managed to start working in C++. I initially had contracts working on train simulators (surprise) and multimedia - I worked on many of the Dorling Kindersley CD-ROM titles and wrote the screensaver games for the Wallace and Gromit Cracking Animator CD. My more recent contracts have been more traditionally IT based, working predominately in C++ on MS Windows NT, 2000. XP, Linux and UN*X. These projects have had wide ranging additional skill sets including system analysis and design, databases and SQL in various guises, C#, client server and remoting, cross porting applications between platforms and various client development processes. I have an interest in the development of the C++ core language and libraries and try to keep up with at least some of the papers on the ISO C++ Standard Committee site at http://www.open-std.org/jtc1/sc22/wg21/.

Education/Credentials

©2016 About.com. All rights reserved.