0

Is there a good practice to access a derived virtual function from it's base class? Here's what I got so far:

class A {
public:
    enum class AType{A,B,C,D};   
    AType aType = AType::A;    
    virtual void Do(){}
};

class B : public A {
public:  
    int val = 0;  
    B(int i) {
        aType = AType::B;
        val = i;
    }   
    void Do(){
        std::cout << val << std::endl;
    }
};

int main(){
    std::map<std::string, A> bunch;  
    bunch["01"] = B(10);
    bunch["02"] = B(15);
    bunch["03"] = B(80);

    for (auto& [k, b] : bunch){
        switch (b.aType){
        case A::AType::A:
            break;
        case A::AType::B:
            static_cast<B&>(b).Do();
            break;
        }
    }
}

I'm not too happy about using a switch there, any advice would be appreciated!

Nick Mack
  • 75
  • 6
  • 5
    a map of `A`s cannot hold `B`s, read about object slicing. Once you fixed that, why do you need the switch? Can't you simply call `b.Do()` ? – 463035818_is_not_an_ai Sep 01 '20 at 13:46
  • 1
    Why wouldn't you just call the virtual method Do() regardless of the type? Or am I missing something? – Andy Nugent Sep 01 '20 at 13:49
  • well, the map is of A type, and when I call do on a B that is in A, it calls A's virtual function instead of B's derived function. I didn't know about slicing, I'm reading, hopefully, I get it right – Nick Mack Sep 01 '20 at 13:50
  • 1
    This should help: [https://stackoverflow.com/questions/44434706/unique-pointer-to-vector-and-polymorphism](https://stackoverflow.com/questions/44434706/unique-pointer-to-vector-and-polymorphism) – drescherjm Sep 01 '20 at 13:59

2 Answers2

2

You should assign a pointer or reference to use polymorphism in C++. If you assign a child class as value, there would be a object slicing. I'd use std::unique_ptr than raw pointer like following, which prints out 10 15 80 as excepted.

#include <memory>
#include <map>

class A {
public:
    enum class AType { A, B, C, D };
    AType aType = AType::A;
    virtual void Do() {}
};

class B : public A {
public:
    int val = 0;
    B(int i) {
        aType = AType::B;
        val = i;
    }
    void Do() override {
        std::cout << val << std::endl;
    }
};

int main() {
    std::map<std::string, std::unique_ptr<A>> bunch;
    bunch["01"] = std::make_unique<B>(10);
    bunch["02"] = std::make_unique<B>(15);
    bunch["03"] = std::make_unique<B>(80);

    for (auto& [k, b] : bunch) {
        b->Do();
    }

    return 0;
}
John Park
  • 1,644
  • 1
  • 12
  • 17
  • @drescherjm, Right, it is no more needed. Good pointing out. – John Park Sep 01 '20 at 14:02
  • 1
    Thanks a lot! I never used unique ptr, will B be deleted by itself if it's removed from the map? I also don't need the enums anymore, this is great ! – Nick Mack Sep 01 '20 at 14:04
  • 2
    Yes the smart pointer will handle the freeing of the objects when needed. And yes you most likely don't need the enum. – drescherjm Sep 01 '20 at 14:05
1

First, what're showing isn't what I'd call "calling from base class". Otherwise, calling derived virtual methods from a base class method is one of the main reasons to use virtual functions. The other reason is to call a method via pointer to base class, like you intend to do.

Also, as mentioned above, in:

bunch["01"] = B(10);

a temporary object of B type is silently cast (sliced) to object of class A. You should use perhaps map of pointers to A.