-1

See following code:

#include <iostream>

struct A {  // Interface
    virtual void a() = 0;
    virtual void x() = 0;
};

struct B {
    virtual void a() { std::cout << "B" << std::endl; }
};

struct C : A, B {
    void a() override { B::a(); }
    void x() override { std::cout << "x" << std::endl; }
};

int main() {
    C c;
    c.a();
    c.x();
}

This code works. The question is if it is the best/optimal solution.

I wonder if there is any trick which allow me not to create a() in C class.

Update: I corrected the code to show why B cannot inherit from A.

Update 2:

#include <iostream>
    
struct I1 {
    virtual void i1() = 0;
};

struct I2 {
    virtual void i2() = 0;
};

struct I3 : I1, I2 {};

struct A : I1 {
    void i1() override { std::cout << "i1" << std::endl; }
};

struct B : A, I2 {
    void i2() override { std::cout << "i2" << std::endl; }
};

int main() {
    B b;
    b.i1();
    b.i2();

    I3* ptr = dynamic_cast<I3*>(&b);  // warning: dynamic_cast of ‘B b’ to ‘struct I3*’ can never succeed
    std::cout << ptr << std::endl;
}

The question is: How to pass pointer to 'b' via interface? Why b cannot be casted to I3 interface?

Jakub
  • 19
  • 5
  • 2
    Yes. Make 'B' inherit from 'A' and skip the multiple inheritance altogether. – Refugnic Eternium Jun 23 '22 at 06:39
  • 1
    If `A` is the interface and `B` shall implement the interface it's good to inherit B from A: `struct B : public A`. Multiple inheritance makes it just more difficult, avoid it if not really necessary. – harper Jun 23 '22 at 06:40
  • 2
    What do you mean by best? Just note that there are two virtual methods, both are overridden by `C::a`, is that what you want? – Quimby Jun 23 '22 at 06:40
  • 4
    best/optional solution for what ? The code you posted just does what it does, but you didnt say what you acutally want to achieve. – 463035818_is_not_an_ai Jun 23 '22 at 06:46
  • I'm thinking if 'using' keyword may be used here. – Jakub Jun 23 '22 at 06:48
  • If you don't implement `C::a()`, then `A::a()` has no overrider and `C` remains an abstract class that you cannot make an instance of. – j6t Jun 23 '22 at 06:51
  • 4
    You can split `A` into 2 interfaces - one can be derived and implemented by `B`. Then `C` can inherit both `B` (for the implementation of `a`) and the new splitted interface (for implementing `x`). – wohlstad Jun 23 '22 at 06:55
  • 2
    You missed [virtual destructor](https://stackoverflow.com/a/461224/4123703) – Louis Go Jun 23 '22 at 06:58
  • 2
    The code in your question achieves its exact functionality. If the requirements were written out, it might become possible to change the code without violating a requirement. However, the requirements are not written out. We have to infer the requirements from the code. This means the code is the ultimate source of truth for what is required, hence it cannot be improved upon. (For example, someone might make an assumption that `B` could inherit from `A`, but then the author makes it known that that assumption was flawed; that aspect of the code could not be changed; it is ultimately correct.) – JaMiT Jun 23 '22 at 07:09
  • 1
    [What is the actual problem?](https://xyproblem.info/) Not the problem with multiple inheritance, the problem you are trying to solve by adding multiple inheritance to the picture. – n. m. could be an AI Jun 23 '22 at 07:17
  • Why do you use inheritance at all? Just make `B::a()` a free function and call it from `C::a()` or copy the printing line into `C::a()`, `B::a()` does not access any data members and is used only in `C::a()`, it just prints out a text. Your example does not show, why you chose that class structure and you gave no explanation, so it is difficult (or easy) to give recommendations, what and how to simplify. Also the interface `A` is not used outside the definition of `C`. – Sebastian Jun 23 '22 at 08:24
  • You have `a()` in `A`, `B` and `C`. Is this logically the same function? Separate `A` into `A1` with `a()` and `A2` with `x()`, so that `B` can inherit from `A1`. Is `A` used statically or dynamically (can the actual class `C` be deduced at compile-time)? Perhaps the use of an interface can be replaced with template functions and/or concepts. – Sebastian Jun 23 '22 at 08:35
  • You want to have a separate inheritance hierarchy for interfaces and implementations? That could work in your simple example case, but will lead to lots of diamonds in general. You would at least need virtual inheritance. Better only inherit interfaces and find another way for the implementation or put some code into the interface, e.g. forwarding the function calls with PImpl. – Sebastian Jun 23 '22 at 13:11
  • 1
    @Jakub your "Update 2" actually changes the question, to which you already have answers. Please post it as a new question and rollback this one. – wohlstad Jun 24 '22 at 12:20

2 Answers2

2

You can consider to split A into 2 interfaces:

struct A1 {  // Interface1
    virtual void a() = 0;
    virtual ~A1() {}
};

struct A2 {  // Interface2
    virtual void x() = 0;
    virtual ~A2() {}
};

This way you can:

  1. Make B inherit from A1 and implement a().
  2. Make C inherit from A2 (to implement x()) and from B to get the implementation for a().

See below:

#include <iostream>

struct B : public A1 {
    virtual void a() override { std::cout << "B" << std::endl; }
};

struct C : public A2, public B {
    virtual void x() override { std::cout << "x" << std::endl; }
};

int main() {
    C c;
    c.a();
    c.x();
}

Note: I added virtual destructors to the base classes. See here why: When to use virtual destructors?.

wohlstad
  • 12,661
  • 10
  • 26
  • 39
  • I omitted virtual destructors to make code simpler. Let's assume that I need to pass object which implements two interfaces to the function . I need then an extra interface to merge this both, right? – Jakub Jun 23 '22 at 09:15
  • This was not mentioned in your question, but the answer is yes. The problem is that then you will need to make `C` inherit both this "merged interface" and `B` and you'll get the inheritance diamond problem. You can see in my question here my attempt to deal with it: https://stackoverflow.com/questions/71818055/can-you-suggest-an-good-alternative-for-multiple-inheritance-in-this-case. – wohlstad Jun 23 '22 at 09:19
  • You can consider to modify your function to accept 2 interfaces, and then pass your `C` instance for both of them. It's really hard to determine the best design without being familair with all the requirements of your system. – wohlstad Jun 23 '22 at 09:21
1

Depending on what you are trying to achieve you could use classes and make the function in B private so no one can try and use it. Something like:

class A {  // Interface
public:
    virtual ~A() {}

    virtual void a() = 0;
    virtual void x() = 0;
};

class B : public A{
private:
    void x() { std::exception("Not Implemented"); };

public:
    ~B() override{}
    void a() override { std::cout << "B" << std::endl; }
};

class C : public B {
public:
    ~C() override{}
    void x() override { std::cout << "x" << std::endl; }
};

Other possible solutions are:

  • Have B as a member of C, so you can call B.a() rather than multiple inheritance to make it a bit less confusing for readers
struct A {  // Interface
    virtual ~A() {}
    virtual void a() = 0;
    virtual void x() = 0;
};

struct B {
    virtual void a() { std::cout << "B" << std::endl; }
};

struct C : A {
    B b;

    virtual ~C() override {}
    void a() override { b.a(); }
    void x() override { std::cout << "x" << std::endl; }
};

int main() {
    C c;
    c.a();
    c.x();
}

You could use function pointers - but this means the functions must be static, and your base class becomes an abstract class

struct A {  // Interface
    virtual ~A() {}

    
    virtual void x() = 0;
    typedef void(*aFunction)();
    aFunction a;

    A() { a = nullptr; }
   
};

struct B {
    static void a() { std::cout << "B" << std::endl; }
};

struct C : A {
    virtual ~C() {}

    void x() override { std::cout << "x" << std::endl; }
    C() { a = B::a; }
};
Natio2
  • 235
  • 1
  • 9