0

I have a class C that inherits both from Ai and B. A and B are unrelated. This is all best explained with the code below. In main(), I have a variable a defined as std::unique_ptr<A>, but initialized with C. I cannot change this definition.

My question is, given a defined like this, how can I call functions defined in B or C or Ai correctly?

#include <memory>

class A 
{ 
public: 
  void fun_a() {}
}; 
  
class B 
{ 
public: 
  void fun_b() {}
}; 

class Ai : public A
{ 
public:
    void fun_ai() {}
}; 
  
class C: public Ai, public B
{ 
public:
    void fun_c() {}
}; 
  
int main() 
{ 
    // I cannot change the following definition:
    std::unique_ptr<A> a = std::make_unique<C>();
    
    a->fun_a();
    //a->fun_b(); // How ?
    //a->fun_c(); // How ?
    //a->fun_ai(); // How ?
    
    return 0; 
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
N.R.
  • 103
  • 1
  • 7

1 Answers1

2

You can static_cast to C*:

static_cast<C*>(a.get())->fun_b();
static_cast<C*>(a.get())->fun_c();
static_cast<C*>(a.get())->fun_ai();

or you could make it polymorphic:

class A { 
public: 
    virtual ~A() = default;
    void fun_a() { std::cout << "fun_a\n"; }
};

and then dynamic_cast:

dynamic_cast<B*>(a.get())->fun_b();
dynamic_cast<C*>(a.get())->fun_c();
dynamic_cast<Ai*>(a.get())->fun_ai();

Note: dynamic_casts to pointer types may fail and return nullptr so, if there's any doubt, check the return value.

Demo

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • If you're using a base class pointer to address a derived class instance, surely all of the classes in the chain should contain virtual destructors as a matter of principle? – Den-Jason Oct 27 '20 at 17:03
  • @RemyLebeau Re `dynamic_cast` very true, I added a note. If I should `static_cast` from `A*` to `B*` I'd need to go via `C` so it's pretty similar to the first option I guess. – Ted Lyngmo Oct 27 '20 at 17:06
  • 1
    @Den-Jason Only if the instance were to be destroyed through a `B*` can I see a problem (and I don't see that happening). The rest of the destructors are already `virtual` automatically. Hmm, come to think of it, even destroyed via a `B*` should it be ok. – Ted Lyngmo Oct 27 '20 at 17:10
  • OK - so at least the `A` class should contain a virtual destructor; the OPs code as it stands could cause undefined behaviour: http://www.gotw.ca/publications/mill18.htm – Den-Jason Oct 27 '20 at 17:19
  • 1
    and (a bit clearer) https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP52-CPP.+Do+not+delete+a+polymorphic+object+without+a+virtual+destructor – Den-Jason Oct 27 '20 at 17:22
  • 1
    @Den-Jason Yes, `A` should have a `virtual` destructor since a `C` is being destroyed through an `A*`. Now, OP:s classes don't have any member variables or virtual methods so I'm not sure if it's UB or not. It's not nice anyway :) – Ted Lyngmo Oct 27 '20 at 17:24
  • Thank you Ted, that worked well. Yes, I do have member variables and virtual methods as well both in A and B. Ai and C do not. Should I define virtual constructors and destructors for A and B ? – N.R. Oct 29 '20 at 07:44
  • 1
    @N.R. Great, you're welcome! When you may need to destroy something through a base class pointer (as in this case), make the base class destructor `virtual`. There is no such thing as `virtual` constructors though. – Ted Lyngmo Oct 29 '20 at 07:56