2

In this code, I have defined three structs, base, derived and MoreDerived each inheriting from the previous member in the list.

However, I have defined the function f() to be virtual in the derived struct, NOT in the base struct.

#include <iostream>
struct base {
    void f() {
       std::cout << "base\n";
   }
};
struct derived : base {
    virtual void f()  { 
        std::cout << "derived\n";
    }
};

struct morederived : derived {
     void f()  { 
        std::cout << "more derived\n";
    }
};

int main()
{
    derived d;
    morederived md;

    base* dp = &d; 
    base* mdp = &md; 
    dp->f(); 
    mdp->f(); 

}

The code above prints base \n base as expected. And if I declare dp and mdp to be pointers of type derived*, the code prints derived \n more derived as expected again, since f is defined to be virtual in the derived.

Of course, this code is small and so the results are predictable. But in large code bases, is doing the above ever a good idea?

Note: I have not seen this in any codes yet, in my limited c++ experience, nor do I have any plans to use such a pattern (or anti-pattern :-D). I am asking this purely out of curiosity.

EDIT: I don't feel this question is not a duplicate to the one pointed out. I am not asking if the virtual keyword is necessary in a derived class. I am asking the effects/plusses/minuses of placing the virtual keyword on a function f() that has an implementation in the base class, but is declared virtual only in the derived class, and then is inherited by other classes. Specifically I am asking if this thing is a patter or anti-pattern.

smilingbuddha
  • 14,334
  • 33
  • 112
  • 189
  • correct me if I'm wrong but I'm pretty sure a structure doesn't count as a class... a structure is just meant to hold data like variables and such so therefore it can't hold functions use an actual class to hold functions... – Chris Hutchison Apr 23 '17 at 23:38
  • 1
    @CJHutchison you are wrong. In C++ classes and structs are almost the same, differ only in default member visibility (private in classes vs public in structs) – Aleksei Petrenko Apr 23 '17 at 23:39
  • "However, I have defined the function f() to be virtual in the derived class, NOT in the base class.". From what I know, overriden functions in derived classes will be `virtual` by default, even if you don't declare them as `virtual` – Fureeish Apr 23 '17 at 23:39
  • @CJHutchison In `C++` `struct` is identical to `class` in everything except the default access control. – Galik Apr 23 '17 at 23:40
  • oh wow didn't know that thanks for the new knowledge! – Chris Hutchison Apr 23 '17 at 23:41
  • 1
    @ CJ Hutchinson Thanks for pointing that out! I have made the edits to say structs instead of classes. However, the question would hold for classes too, since structs are just classes where everything is public by default instead of private as it is for classes. – smilingbuddha Apr 23 '17 at 23:41
  • Possible duplicate of [C++ "virtual" keyword for functions in derived classes. Is it necessary?](http://stackoverflow.com/questions/4895294/c-virtual-keyword-for-functions-in-derived-classes-is-it-necessary) – Fureeish Apr 23 '17 at 23:42
  • but wouldnt you just have to do morederived.f()? I'm not too great at c++ as well lol so this is something I'm curious about as well – Chris Hutchison Apr 23 '17 at 23:42
  • 1
    @Fureeish - C++ is different from Java and functions are not virtual unless you explicitly say so. – Bo Persson Apr 23 '17 at 23:43
  • @BoPersson, see the question I flagged as possible duplicate and accepted answer – Fureeish Apr 23 '17 at 23:44
  • Make sure to use the `override` keyword to make it clearer. –  Apr 23 '17 at 23:45
  • 1
    It's not a pattern if it doesn't have a use case. What is its use case? – Galik Apr 23 '17 at 23:46
  • 2
    @Fureeish The question you linked asks if `derived::f()` needs to be declared `virtual` when `base::f()` is `virtual`; in this case, `derived::f()` overrides `base::f()`. This question is about whether declaring `derived::f()` virtual when `base::f()` is _not_ `virtual` is good or bad design; in this case, `derived::f()` hides `base::f()`. They're actually much more different than they look. – Justin Time - Reinstate Monica Apr 24 '17 at 01:31

2 Answers2

2

Yes, this breaks the Is-a contract. Your derived is not playing by the same rules as base.

This is never a good idea. You might want to consider composition for derived. I.e.

struct derived {
    base b; // for whatever use you might have for base.
    virtual void f()  { 
        std::cout << "derived\n";
    }
};
Captain Giraffe
  • 14,407
  • 6
  • 39
  • 67
1

In my book, it qualifies as a code smell.

derived::f() actually hides base::f() rather than overriding it.

This means that base_ptr->f() always calls base::f(), even if base_ptr points at an instance of a derived class, whereas derived_ptr->f() will use virtual function dispatch.

The fact of making derived::f() virtual and documenting that it can be overridden, encourages implementers of classes like morederived to expect that a call of the form pointer->f(), if pointer points at an instance of morederived, to believe their version will be called. In reality, which version will be called depends on the type of pointer (e.g. different version called if it is base * or derived *).

That can result in quite an unpleasant surprise for anyone who uses the classes, and doesn't closely inspect all classes in the hierarchy. In large class hierarchies, particularly if provided by someone else, developers will not typically inspect a complete class hierarchy provided by someone else that closely. The will assume it works as advertised, if they care at all about their productivity.

If this sort of thing was present in a commercial class library, the vendor could expect to receive bug reports from customers bitten by such a bug, to lose customers of their library (on the premise that they introduced a bug through bad development technique), or both.

Peter
  • 35,646
  • 4
  • 32
  • 74