Is it ever "moral" to override a nonvirtual function? [closed]
I have used the following C++ rule of thumb for a long time:
If a class overrides a function in its base class, the function should be declared
virtual
in the base.
I think I have come across an exception from this rule. To judge whether this is justified, or points at a flaw in my design, I am asking this question. I would like to get examples or better rules.
Edit: I tried describing my use case here, and I have understood that I don't really need inheritance!
I wanted to ask a general question though. Thanks for the answers!
You can not override a non-virtual function. Only thing you can do is to hide the base class implementation. But this doesn't provide you the polymorphic behavior that virtual function provide.
I personally don't like this, but sometimes it's useful. The Standard library uses this too:
stringstream ss;
/* Imagine you want to redirect all output that goes into "ss"
* to "cout". The following does NOT work! */
ss.rdbuf(cout.rdbuf());
Why does it not work? Because stringstream
has hidden ios::rdbuf
with a same named function that only provides read access to its internal std::stringbuf
, rather than to the attached buffer. You need to do the following
ss.std::ios::rdbuf(cout.rdbuf());
Now, the buffer attached to the stream is not equal to what ss.rdbuf()
returns. I personally dislike this design though.
I once had a good use of hiding though. In my opinion, hiding needs one requirement:
- The behavior should be the same.
In my case, I had a base class like this (not really near as it was, but it's conveying the situation).
template<typename T>
struct A {
void doSomething() {
T t;
t.doIt();
}
};
class Foo;
struct B : A<Foo> {
};
B b;
What happens when one calls b.doSomething()
? It needs the header of Foo
, because the function wants to call doIt
on the class and creates a variable of that type. The workaround was simple
class Foo;
struct B : A<Foo> {
void doSomething();
};
// and in the .cpp file:
#include "Foo.h"
void B::doSomething() {
A<Foo>::doSomething();
}
This way, I prevented that every user of my B
class needs to include the Foo's header. Only B's cpp file, which knows it depends on "Foo", has to do this.
I think that you misremembered the rule. The rule is: "If you are overriding a virtual method from the base class, then the overriding method should be declared virtual."
It is a code style rule, that prevents confusion since virtual modifier is inherited.
Is it possible? Yes. Is it moral? That depends on your definition of morals. Is it confusing to your fellow developers and future maintenance programmers? A definite yes!
Having a function in a derived class with the same name as a non-virtual function in a base class, just hides the base class function and its overloads.
I believe that this is an abuse of inheritance, since you are basically saying that you want to redefine the way something is done in a way that the base class contract does not allow you to. By making a function non-virtual in a base class, you are specifying what you want a function to do (its interface) and more importantly how you want it to do it (its implementation). The implication of making the function non-virtual is that neither its interface nor its implementation should be changed in a derived class.
You can do it. But may be that not what you want. One of the main tenet of OOPs is runtime-polymorphism. In this case you may not able be use that.
Check following code. Its trying to use base type object to handle the super-type objects. But incase of non virtual its not working.
My expected output was
In B::printNonV()
In B::printV()
But I got
In A::printNonV()
In B::printV()
.
#include <iostream>
using namespace std;
class A
{
public:
void printNonV(){
cout<<"In A::printNonV() "<<endl;
}
virtual void printV(){
cout<<"In A::printV()"<<endl;
}
};
class B:public A
{
public:
void printNonV(){
cout<<"In B::printNonV()"<<endl;
}
virtual void printV(){
cout<<"In B::printV()"<<endl;
}
};
int main(){
A* b=new B();
b->printNonV();
b->printV();
}
One example where you'd overload (not override) a non-virtual function from a base class is when you're using CRTP to implement simulated dynamic binding:
// in the shared header
template <typename Derived>
struct GenericOSDetails {
size_t preferred_character_size() {
return 1; // we expect `char` to be the preferred character type
}
size_t preferred_string_length(size_t numchars) {
return numchars * static_cast<Derived&>(*this).preferred_character_size();
}
// other functions that do considerably more useful things based on
// the preferred character size and encoding.
};
// in the linux header
struct LinuxOSDetails : GenericOSDetails<LinuxOSDetails> {
// we're happy with the defaults.
};
// in the windows header
struct WindowsOSDetails : GenericOSDetails<WindowsOSDetails> {
// configure ourselves for "Unicode" vs non-Unicode builds.
size_t preferred_character_size() {
return sizeof(TCHAR);
}
};
Note simulated dynamic binding - with this technique, instances of WindowsOSDetails will not be passed around as pointers to the base class GenericOSDetails<WindowsOSDetails>
, so there is no need for virtual functions. Static binding is used throughout, but the base class can still call derived class functions and get the overloaded version.
To be honest I'm not sure how practically useful this is. Probably 99% of the times that you might think of using it it's either a premature optimization, or else you should provide a strategy as a template argument, together with a default strategy for the common case, and not use inheritance at all. But the other 1% of the time, and in general any time when you want to use inheritance but you don't want or need dynamic polymorphism, then you can avoid virtual functions if you want to.
AFAIK it's only really in template-heavy code that you'd do anything particularly interesting with inheritance that doesn't rely on dynamic polymorphism. Your ordinary OOP paradigm isn't interested.
I think that it is generally agreed upon that hiding base class functionality is not a good thing and should be done very rarely if ever. One of the more surprising problems is that you really break polymorphic behavior (as mentioned by Asha). Here's an example of why that tends to be surprising.
struct Person {
virtual std::string get_name() const = 0;
void print_name(std::ostream& s) const { s << get_name() << std::endl; }
};
struct Fred: Person {
virtual std::string get_name() const { return "Fred"; }
};
struct Barney: Person {
virtual std::string get_name() const { return "Barney"; }
void print_name(std::ostream& s) const { s << "Bam Bam" << std::endl; }
};
std::ostream& operator<<(std::ostream& s, Person const& p) {
p.print_name(s);
return s;
}
int main() {
Fred fred;
Barney barney;
barney.print_name(std::cout);
std::cout << fred << barney << std::endl;
return 0;
}
This outputs:
Bam Bam
Fred
Barney
Hiding the base class breaks the Liskov Substitution Principle which surprises implementations in various and usually unpleasant ways.
精彩评论