iHowell
iHowell

Reputation: 2447

How to extend an inherited class's inherited class

Suppose I have this minimal example:

class BaseClass {
    void method1();
};

class Foo : public BaseClass {
    void method1();
};

class Bar : public Foo {
    void method1();
}

class Interface : public Foo {
};

class MyClass : public Interface, public Bar {
}

When implementing MyClass, how can I tell the compiler that Bar is extending the Foo in Interface? I keep getting compiler errors due to the ambiguous conversion.

Note: Foo and Bar are from a library, so I can't implement another interface just to handle this.

Upvotes: 0

Views: 59

Answers (1)

Aconcagua
Aconcagua

Reputation: 25516

class Foo
{
public:
    virtual ~Foo() { }
    virtual void f() { std::cout << "foo!" << std::endl; }
};

class Bar : public Foo
{
public:
    void f() override { std::cout << "bar!" << std::endl; }
};

Problem now is that you cannot inherit from Foo in Interface: You cannot modify Bar, thus you cannot make it inherit virtually, so even if Interface did, you'd get two instances of Foo in MyClass. So my approach is having a reference to Foo within interface and provide an explicit cast to:

class Interface
{
    Foo& foo;
protected:
    Interface(Foo& foo) : foo(foo) { }
public:
    operator Foo&()
    {
        return foo;
    }

    virtual ~Interface() { }

    // this actually is only a short cut - you can always
    // access Foo's f via cast as well!
    // (so you can drop it, if you prefer)
    virtual void f() { foo.f(); }
};

class MyClass : public Interface, public Bar
{
public:
    MyClass() : Interface(*static_cast<Foo*>(this)) { }
    using Bar::f;
};

Now you can use it as follows:

MyClass c;
Interface* i = &c;
Foo* f = &static_cast<Foo&>(*i);
// or, if you have not yet lost access to c, simply:
f = &static_cast<Foo&>(c);

Extension: If you need to be able to instantiate Interface directly (not in form of a derived class), you can achieve this with some minor modifications to Interface:

class Interface
{
    Foo* foo; // raw pointer even in times of C++11 and smart pointers:
              // need to be able to delete  c o n d i t i o n a l l y
    bool isOwner;
protected:
    Interface(Foo& foo) : foo(&foo), isOwner(false) { }
public:
    Interface() : foo(new Foo()), isOwner(true) { }

    operator Foo&()
    {
        return *foo;
    }

    virtual ~Interface()
    {
        if(isOwner)
        {
            delete foo;
        }
    }

    virtual void f() { foo->f(); }
};

Edit: While above would work in general, you would get in trouble if you try to delete an Interface (not derived) via Foo pointer. You can solve the issue as follows:

class Interface
{
    Foo& foo;
protected:
    Interface(Foo& foo) : foo(foo) { }
public:

    operator Foo&()
    {
        return foo;
    }

    virtual ~Interface() { }

    //virtual void f() { foo.f(); }
};

class MyFoo : public Interface, public Foo
{
public:
    MyFoo() : Interface(*static_cast<Foo*>(this)) { }
    virtual ~MyFoo() { }
    //using Foo::f; // don't need, if dropping the short cut
};

class MyBar : public Interface, public Bar
{
public:
    MyBar() : Interface(*static_cast<Foo*>(this)) { }
    virtual ~MyBar() { }
    //using Bar::f; // don't need, if dropping the short cut
};

While now Foo inherits from Bar, MyBar does not from MyFoo, so you cannot assign a MyBar object to a MyFoo pointer. But you can both assign (via the cast) to a Foo pointer, which is, according to the discussion to question, your actual goal, so this should be fine...

Upvotes: 1

Related Questions