Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

calling child methods from parent pointer with different child classes

I've a parent class with 2 or more child class deriving from it. The number of different child classes may increase in future as more requirements are presented, but they'll all adhere to base class scheme and will contain few unique methods of their own. Let me present an example -

#include <iostream>
#include <string>
#include <vector>
#include <memory>

class B{
    private: int a; int b;
    public: B(const int _a, const int _b) : a(_a), b(_b){}
    virtual void tell(){ std::cout << "BASE" << std::endl; }
};

class C : public B{
    std::string s;
    public: C(int _a, int _b, std::string _s) : B(_a, _b), s(_s){}
    void tell() override { std::cout << "CHILD C" << std::endl; }
    void CFunc() {std::cout << "Can be called only from C" << std::endl;}
};

class D : public B{
    double d;
    public: D(int _a, int _b, double _d) : B(_a, _b), d(_d){}
    void tell() override { std::cout << "CHILD D" << std::endl; }
    void DFunc() {std::cout << "Can be called only from D" << std::endl;}
};

int main() {
    std::vector<std::unique_ptr<B>> v;

    v.push_back(std::make_unique<C>(1,2, "boom"));
    v.push_back(std::make_unique<D>(1,2, 44.3));

    for(auto &el: v){
        el->tell();
    }
    return 0;
}

In the above example tell() method would work correctly since it is virtual and overrided properly in child classes. However for now I'm unable to call CFunc() method and DFunc() method of their respective classes. So I've two options in my mind -

  • either packup CFunc() and friends inside some already defined virtual method in child class so that it executes together. But I'll loose control over particular execution of unique methods as their number rises.

  • or provide some pure virtual methods in base class, which would be like void process() = 0 and let them be defined in child classes as they like. Would be probably left empty void process(){} by some and used by some. But again it doesn't feels right as I've lost return value and arguments along the way. Also like previous option, if there are more methods in some child class, this doesn't feels right way to solve.

and another -

  • dynamic_cast<>?. Would that be a nice option here - casting back parent's pointer to child's pointer (btw I'm using smart pointers here, so only unique/shared allowed) and then calling the required function. But how would I differentiate b/w different child classes? Another public member that might return some unique class enum value?

I'm quite unexperienced with this scenario and would like some feedback. How should I approach this problem?

like image 653
Abhinav Gauniyal Avatar asked Sep 18 '16 21:09

Abhinav Gauniyal


1 Answers

I've a parent class with 2 or more child class deriving from it... But I'll loose control over particular execution of unique methods as their number rises.

Another option, useful when the number of methods is expected to increase, and the derived classes are expected to remain relatively stable, is to use the visitor pattern. The following uses boost::variant.

Say you start with your three classes:

#include <memory>
#include <iostream>

using namespace std;
using namespace boost;

class b{};
class c : public b{};
class d : public b{};

Instead of using a (smart) pointer to the base class b, you use a variant type:

using variant_t = variant<c, d>;

and variant variables:

variant_t v{c{}};

Now, if you want to handle c and d methods differently, you can use:

struct unique_visitor : public boost::static_visitor<void> {
    void operator()(c c_) const { cout << "c" << endl; };
    void operator()(d d_) const { cout << "d" << endl; };
};

which you would call with

apply_visitor(unique_visitor{}, v);

Note that you can also use the same mechanism to uniformly handle all types, by using a visitor that accepts the base class:

struct common_visitor : public boost::static_visitor<void> {
    void operator()(b b_) const { cout << "b" << endl; };
};

apply_visitor(common_visitor{}, v);

Note that if the number of classes increases faster than the number of methods, this approach will cause maintenance problems.


Full code:

#include "boost/variant.hpp"
#include <iostream>

using namespace std;
using namespace boost;

class b{};
class c : public b{};
class d : public b{};

using variant_t = variant<c, d>;

struct unique_visitor : public boost::static_visitor<void> {
    void operator()(c c_) const { cout << "c" << endl; };
    void operator()(d d_) const { cout << "d" << endl; };
};

struct common_visitor : public boost::static_visitor<void> {
    void operator()(b b_) const { cout << "b" << endl; };
};

int main() {
    variant_t v{c{}};
    apply_visitor(unique_visitor{}, v);
    apply_visitor(common_visitor{}, v);
}
like image 197
Ami Tavory Avatar answered Sep 21 '22 19:09

Ami Tavory