4

I would to block child classes from overriding a base method and have the child classes override a new method in a parental class. In other words, a child class of the base class blocks the base class methods and delegates to a new method that further child classes must override. I still want the base class method to be available.

Here is an example:

#include <iostream>
#include <string>

struct Base
{
    virtual const std::string&  class_name(void) = 0;
};

struct Level1
    : public Base
{
private:  // Prevent child classes from overriding
          //     the Base::class_name method 
    const std::string& class_name(void)
        {
            static std::string name;
            name = "class" + class_name_from_level_1();
            return name;
        }
protected:
    // This is the "new" or redirected class that child classes
    //    must override.
    virtual const std::string& class_name_from_level_1(void) = 0;
};

struct Level2
    : public Level1
{
    static std::string  name;

    const std::string&  class_name_from_level_1(void)
        {
            if (name.length() == 0)
            {
                name = "Level2";
            }
            return name;
        }
};


int main(void)
{
    Level2  lev2;
    std::cout << lev2.class_name() << "\n";
    return 0;
}

I am getting the following errors from g++:

$ g++ hiding_virt_methods.cpp -o hiding_virt_methods.exe
hiding_virt_methods.cpp: In function `int main()':
hiding_virt_methods.cpp:15: error: `virtual const std::string& Level1::class_name()' is private
hiding_virt_methods.cpp:43: error: within this context

In the above example, I want the following chain of execution for Level2:
Base::class_name() --> Level1::class_name_from_level_1() --> Level2::class_name_from_level_1()

Also, I only want to block inheritance of specific methods in the Base class. Protected and Private Inheritance affect all the public methods.

So how do I stop the chain of inheritance of specific Base methods at different levels in the inheritance tree?

Edit: Real world example.
I have an interface class Record. Class Record_With_Id inherits from class Record and adds an ID field. The class Record contains an accept_visitor method. Class Record_With_Id overrides accept_visitor to apply to the ID field, then calls a virtual method, record_with_id_accept_visitor, which descendants must implement.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154

5 Answers5

2

For your immediate problem, you can rename your class_name() functions to class_name_impl() or similar, then in the base class have a class_name() function that calls the implementation one. That way, only the base class version will match when calling class_name() on a derived object.

More generally, you can frustrate attempts to call the base class methods by having same-named functions in the derived classes - as you've done, but anyone can cast to a Base& and call whatever they like. You can't stop virtual methods being overridable in derived classes... you can only frustrate their use.

It's worth remembering that a publicly derived class IS an instance of the base class, and SHOULD provide the base class's interface.

EDIT: re yout "real world example" edit, can you explain the problem with a normal implementation ala...

#include <iostream>

struct Visitor
{
    virtual void operator()(int&) const = 0;
};

struct X
{
    virtual void visit(Visitor& v) { v(a); v(b); }
    int a;
    int b;
};

struct X_with_C : X
{
    int c;
    virtual void visit(Visitor& v) { X::visit(v); v(c); }
};

struct My_Visitor : Visitor
{
    void operator()(int& n) const { std::cout << ++n << '\n'; }
};

int main()
{
    X x;
    x.a = 10;
    x.b = 20;
    My_Visitor visitor;
    x.visit(visitor);
    X_with_C xc;
    xc.a = -10;
    xc.b = -20;
    xc.c = -30;
    xc.visit(visitor);
    X& rx = xc;
    rx.visit(visitor);
}

Output:

11
21
-9
-19
-29
-8
-18
-28
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • In your example, the leaf class method calls the parent method, going "up" the inheritance tree. In my example, the parent method calls the child method, going "down" the tree. I will try changing the calling "direction" and see if it resolves the issue. – Thomas Matthews Oct 21 '10 at 14:02
2

hasn't C++11 added final and override?

http://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final

1

Four years later, let me add that C++11 has introduced keyword final:

class Base final {

This can also be applied on the virtual methods:

class Base{
 protected: 
        virtual void doWork() = 0;
 public:
        virtual void startWork() final { doWork(); }

};


class Derived: public Base{
 protected:
        virtual void doWork() override { /* some work */ }
 public:
       //  error: overriding final function ‘virtual void Base::startWork()’
        virtual void startWork() override { /* something else */ } 


};
Nemanja Boric
  • 21,627
  • 6
  • 67
  • 91
0

Visual Studio 2005 and above implement a keyword "sealed", which is a Microsoft extension to C++. You put it in the declaration of Level1::class_name(). I don't think there is a portable way.

Eugene Smith
  • 9,126
  • 6
  • 36
  • 40
  • I would never recommend using the so called "extensions", they are not part of C++ whichever way you look –  Feb 20 '12 at 10:55
  • 1
    And depending on MS extensions is even worse than depending on GNU extensions: With MS extensions, you rely on MS to sell you licences of their software. If they decide to dump support for the extension in the next release, you are doomed. With GNU extensions, you rely on free software; software that you are free to download the sources, free to compile them, free to fork them if need be, free to do whatever you need to do to use the extension you want, even if the GNU guys should cut support for this feature. – cmaster - reinstate monica Apr 12 '14 at 08:42
0

It appears that you're trying to do something in a way that's hard.

Depending on what it is that you're trying to achieve, the following may be a solution.

#include <iostream>
#include <string>

struct Base
{
    virtual std::string class_name() const = 0;
};

class Level1
    : public Base
{
public:
    std::string class_description() const
    {
        return "class " + class_name();
    }
};

class Level2
    : public Level1
{
public:
    virtual std::string class_name() const
    {
       return "Level2";
    }
};


int main()
{
    Level2  lev2;
    std::cout << lev2.class_description() << "\n";
}

In the above code I've assumed it's for debugging/tracing or something like that. For id purposes look into typeid (a built-in operator).

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • @Alf P. Steinbach: This seems to the opposite of what the OP asked at Level1: "Prevent child classes from overriding the Base::class_name method". You seem to be overriding the class_name in Level2. –  Oct 21 '10 at 02:54
  • 1
    @Shiftbit: Oh, it has nothing to do with preventing overriding, which was the OP's imagined solution to some problem. It has to do with (hopefully) solving the problem, which unfortunately was not described. Explaining a bit more: it shows how, by giving different things different names, like `class_name` versus `class_description`, one can avoid problems stemming from those things having the same name. :-) Cheers & hth., – Cheers and hth. - Alf Oct 21 '10 at 02:58
  • @Alf: The idea is to alter the inheritance chain or tree. Level_1 blocks child classes from overriding the base class method while presenting a new method that child classes must implement. The Level_1 performs it's functionality then delegates to the new pure virtual method. – Thomas Matthews Oct 21 '10 at 13:57
  • @Thomas: I'm still not sure exactly what you're trying to solve, but the only way to "stop" overriding portably is to either make the whole class final (see the FAQ for the ugly portable solution) or use containment instead of inheritance, i.e. Level1 as unrelated to Base. Perhaps if some common functionality is needed a common pure interface can be defined. But in your example there seems to be nothing between Level1 and Base, no inheriting further up that must stopped down at the Level1 level. So it seems there's something more. Perhaps if you tried to present more concrete code? – Cheers and hth. - Alf Oct 21 '10 at 14:15