Search code examples
c++inheritanceinterfacemultiple-inheritance

A pointer to a combined interface - point to a class implementing one and extending from an implementation of the other


I have two interfaces:

class FirstInterface
{
    virtual int getId() const = 0;
};

class SecondInterface
{
    virtual void setId(int id) = 0;
};

This is a combined interface:

class CombinedInterface : public FirstInterface, public SecondInterface
{

};

This is a concrete class of first interface:

class FirstConcrete : public FirstInterface
{
    virtual int getId() const
    {
        return 1;
    }
};

Now, this class CompleteConcrete should have the CombinedInterface but want to reuse the implementation of FirstConcrete at the same time.

class CompleteConcrete : public FirstConcrete, public SecondInterface
{
    virtual void setId(int id) { }
};

// This is wrong C++
// Cannot convert from CompleteConcrete * to CombinedInterface *
// CombinedInterface * combinedInterface = new CompleteConcrete();

This is not working of course. Does anyone know a way to achieve this goal in C++ ??


Solution

  • Here's the virtual-inheritance based solution that I mentioned in the comments:

    class FirstInterface
    {
        virtual int getId() const = 0;
    };
    
    class SecondInterface
    {
        virtual void setId(int id) = 0;
    };
    
    
    class CombinedInterface : virtual public FirstInterface,
                  virtual public SecondInterface
    {
    
    };
    
    class FirstConcrete : virtual public FirstInterface
    {
        virtual int getId() const
        {
            return 1;
        }
    };
    
    class CompleteConcrete : virtual public FirstConcrete,
                 virtual public CombinedInterface
    {
        virtual void setId(int id) { }
    };
    
    void example()
    {
        CombinedInterface * combinedInterface = new CompleteConcrete();
    }
    

    With virtual inheritance, the only change that's needed (besides the elephant in the room) is to have CompleteConcrete multiply-inherit from CombinedInterface, instead of SecondInterface. You can think of it this way: with CompleteConcreate in the picture, it now supports CombinedInterface, and not just the addition of SecondInterface.

    Some frown on virtual inheritance. I don't. It's one of the unique features of C++, that no other high level language shared, TMK. It's a very powerful tool, and can solve certain problems that would be hard to solve in other ways. The two main disadvantages of virtual inheritance are:

    1. Because it is so powerful, it can be easily misused, and lead to various problems.

    2. If virtually-inherited classes have non-default constructors it quickly becomes painful, because every class that virtually inherits something is now responsible for constructing it.

    But as long as virtual inheritance is used correctly, and the involved classes can take of constructing themselves, virtual inheritance is a useful tool.

    P.S. I'll also mention one other alternative solution that just came to mind. If, say you have your CombinedInterface just so that it can be required for some particular function, like:

     void somefunction(CombinedInterface &object);
    

    Your function requires a combined interface.

    Make a small change:

     void somefunction(FirstInterface &first, SecondInterface &second);
    

    and pass the same object as both parameters. You can pass CompleteConcrete, that implements both interfaces, without any changes to your class hierarchy. You could also have a template facade that makes it look like the function still takes one parameter:

    template<typename T> void somefunction(T &&t)
    {
        real_somefunction(std::forward<T>(t), std::forward<T>(t));
    }
    
    void real_somefunction(FirstInterface &first, SecondInterface &second);
    

    You can pretty much get rid of CombinedInterface, and simply pass any object that implements both interfaces to somefunction(), and your real_somefunction() will use one or the other parameter to invoke the appropriate interface.

    Say you need to carry a pointer to an object that implements both interfaces?

    class combined_pointer : public std::pair<FirstInterface *, SecondInterface *> {
    
    public:
        template<typename T> combined_pointer(T *t)
                : std::pair<FirstInterface *, SecondInterface *>(t, t)
        {}
    };
    

    Just a starting point.