Abhinav Gauniyal Abhinav Gauniyal - 2 months ago 22
C++ Question

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?

Answer

You can declare interfaces with pure methods for each device class. When you define a specific device implementation, you inherit only from the interfaces that make sense for it.

Using the interfaces that you define, you can then iterate and call methods which are specific to each device class.

In the following example I have declared a HardwareInterface which will be inherited by all devices, and an AlertInterface which will be inherited only by hardware devices that can physically alert a user. Other similar interfaces can be defined, such as SensorInterface, LEDInterface, etc.

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

class HardwareInteface {
    public:
        virtual void on() = 0;
        virtual void off() = 0;
        virtual char read() = 0;
        virtual void write(char byte) = 0;
};

class AlertInterface {
    public:
        virtual void alert() = 0;
};

class Buzzer : public HardwareInteface, public AlertInterface {
    public:
        virtual void on();
        virtual void off();
        virtual char read();
        virtual void write(char byte);
        virtual void alert();
};

void Buzzer::on() {
    std::cout << "Buzzer on!" << std::endl;
}

void Buzzer::off() {
    /* TODO */
}

char Buzzer::read() {
    return 0;
}

void Buzzer::write(char byte) {
    /* TODO */
}

void Buzzer::alert() {
    std::cout << "Buzz!" << std::endl;
}

class Vibrator : public HardwareInteface, public AlertInterface {
    public:
        virtual void on();
        virtual void off();
        virtual char read();
        virtual void write(char byte);
        virtual void alert();
};

void Vibrator::on() {
    std::cout << "Vibrator on!" << std::endl;
}

void Vibrator::off() {
    /* TODO */
}

char Vibrator::read() {
    return 0;
}

void Vibrator::write(char byte) {
    /* TODO */
}

void Vibrator::alert() {
    std::cout << "Vibrate!" << std::endl;
}

int main(void) {
    std::shared_ptr<Buzzer> buzzer = std::make_shared<Buzzer>();
    std::shared_ptr<Vibrator> vibrator = std::make_shared<Vibrator>();

    std::vector<std::shared_ptr<HardwareInteface>> hardware;
    hardware.push_back(buzzer);
    hardware.push_back(vibrator);

    std::vector<std::shared_ptr<AlertInterface>> alerters;
    alerters.push_back(buzzer);
    alerters.push_back(vibrator);

    for (auto device : hardware)
        device->on();

    for (auto alerter : alerters)
        alerter->alert();

    return 0;
}

Interfaces can be even more specific, as per individual sensor type: AccelerometerInterface, GyroscopeInterface, etc.