views:

121

answers:

3

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.

A: 

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.

http://stackoverflow.com/questions/2260055/c-virtual-sealed-function
Shiftbit
+2  A: 

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
@Tony: 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
+1  A: 

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.,

Alf P. Steinbach
Do you mind explaining a bit more?
Shiftbit
@Shiftbit: OK, but what?
Alf P. Steinbach
@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.
Shiftbit
Alf P. Steinbach
@Alf: ahh... makes sense! Thank you.
Shiftbit
@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
@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?
Alf P. Steinbach