Is it ever "moral" to override a nonvirtual function? [closed]
Asked Answered
G

7

9

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!

Given answered 13/3, 2011 at 13:50 Comment(8)
IMHO, I think if there is a justification for "hiding", and you know what you are doing, why not?Spearhead
why don't you tell us why you think your case is an exception? Then we can say if we agree, and if not, why not. Until then, this is really a non-question.Zeuxis
What do you mean by "morale"?. I won't work as you wish in some cases.Ir
@Zimbabao, The "morale" is low at the moment, we're not sure whether to hide or not to hide... (it's a little different to "moral" - which @jalf described well... ;) )Spearhead
I still think that you simply misremembered the original rule, but as you state it, yes it is perfectly OK.Kirakiran
Virtual functions can be overridden. Non-virtual ones cannot. Therefore this is a non-Q. Vote to close.Wadley
@David it's clear that the questioner means "hiding". I doubt he has ever read the Standard as he's a beginner. Closing a question based on not 100% correct terminology or use of the English language, I guess we would need to close like 90% of all questions here. Rather than closing the question, I would like to ask @Given to give a short code example.Brough
It's still still a good question. I don't think closing question is right thing to do. It just needs a little bit of editing. The question should be something like "What is the difference of overriding virtual vs non-virtual method? When overloaded method should not be declared as virtual?". It's a good question for beginners to read. Zimbabao's answer is a good, simple answer to that question.Properly
B
11

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.

Brough answered 13/3, 2011 at 13:58 Comment(1)
+1 I had no idea you could call a method in a base class from outside by using derived.Base::method(args...).Oyer
C
12

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.

Chime answered 13/3, 2011 at 13:54 Comment(0)
B
11

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.

Brough answered 13/3, 2011 at 13:58 Comment(1)
+1 I had no idea you could call a method in a base class from outside by using derived.Base::method(args...).Oyer
A
2

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.

Apodaca answered 13/3, 2011 at 13:54 Comment(0)
A
2

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.

Arcadian answered 13/3, 2011 at 13:57 Comment(2)
+1 I guess this is what the OP actually meant.Wadley
No, i didn't intend to ask about the coding-style rule; i meant the design rule.Given
I
2

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();
}
Ir answered 13/3, 2011 at 14:10 Comment(1)
Good point. In other words it's not a rule to always use virtual when a method is overridden. It is a rule to use virtual when you want this specific behaviour (most cases). If overriding non-virtual method was not useful it would be reported as a error by compillers. I still can't think of a situation when overriding non-virtual method is useful, but there might be.Properly
L
2

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.

Locution answered 13/3, 2011 at 14:38 Comment(0)
C
1

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.

Classy answered 13/3, 2011 at 14:12 Comment(2)
But having a base class can be an implementation detail, and doesn't necessarily mean that the classes are used polymorhically. Then it could be ok.Pulliam
@BoPersson - yes. But you would use private or protected inheritance (in C++) or extend a package private class in Java. If the inheritance is visible, then it isn't an implementation detail since someone can (and will) depend on it.Classy

© 2022 - 2024 — McMap. All rights reserved.