-2

In the following, I expected class Child's protected field member _AorB to be of type B, and not A, but reality shows otherwise.

What am I mis-understanding, and how can I adjust the code for the desired behavior?

class A{
    public:
       void doit(){
           std::cout<<" this is A!"<<std::endl;
       }
    
};

class B{
    public:
       void doit(){
           std::cout<<" this is B!"<<std::endl;
       }
    
};

class Parent{
    public:
      void doit(){
          _AorB.doit();
      }
      protected:
        A _AorB;
};

class Child: public virtual Parent{
    protected:
      B _AorB;
};

int main()
{
    cout<<"Hello World";
    auto c = Child();
    c.doit(); // I expected this to print "This is B" because c is Child(), and Child class's _AorB is of type B.
    return 0;
}
user1008636
  • 2,989
  • 11
  • 31
  • 45
  • 2
    `_AorB.doit()` is calling `Parent::_AorB.doit()`, it does nothing know about `Child::_AorB.doit()`. – 273K Oct 25 '22 at 17:35
  • 3
    `c` may be a `Child`, but this doesn't change the fact that `Parent::doit` just considers its own members. You cannot overwrite a member variable. The only thing that happens here is you shadowing the version of `_AorB` in the `Parent`. There are still 2 seperate members named `_AorB` in each `Child` object. – fabian Oct 25 '22 at 17:37
  • 1
    Does this answer your question? [Why doesn't C++ have virtual variables?](https://stackoverflow.com/questions/3248255/why-doesnt-c-have-virtual-variables) – fabian Oct 25 '22 at 17:38
  • @fabian i see. so what changes can I make to allow a child class a different behavior or variable from the parent class? – user1008636 Oct 25 '22 at 17:40
  • punting `virtual` keyword in random place is not a good approach. What this `: public virtual Parent` suppose to do? – Marek R Oct 25 '22 at 17:47
  • @MarekR to handle the diamond pattern if different child classes are subclassed by another class – user1008636 Oct 25 '22 at 17:49
  • 1
    Diamond is not "a pattern", but "a problem". Also your example doesn't have a diamond inheritance, so this `virtual` it just information noise. – Marek R Oct 25 '22 at 17:50
  • This doesn't address the question, but names that begin with an underscore followed by a capital letter (`_AorB`) and names that contain two consecutive underscores are reserved for use by the implementation. Don't use them in your code. – Pete Becker Oct 25 '22 at 18:52

2 Answers2

0

You can make such changes:

template <typename AorB>
class Parent{
    public:
      void doit(){
          _AorB.doit();
      }
      protected:
        AorB _AorB;
};

class Child: public virtual Parent<B> {
}

Also take a look at What are the rules about using an underscore in a C++ identifier?

  • Reserved in any scope, including for use as implementation macros:
    • identifiers beginning with an underscore followed immediately by an uppercase letter
273K
  • 29,503
  • 10
  • 41
  • 64
0

273K's answer is excellent.

Depending on what kind of problem you are trying to solve and how the data is held in the hierarchy, you could use a std::variant<A, B> to allow "flippy" behavior based on the type, and access that member variable through a virtual getter member function.

#include <iostream>
#include <variant>

class A {
public:
    void doit() {
        std::cout << " this is A!\n";
    }
};

class B {
public:
    void doit() {
        std::cout << " this is B!\n";
    }
};

class Parent {
public:
    virtual ~Parent() = default;
    void doit() {
        auto ab = get_AorB();
        std::visit([](auto arg) { arg.doit(); }, ab);
    }
    virtual auto get_AorB() -> std::variant<A, B> {
        return _a;
    }
protected:
    A _a;
};

class Child : public virtual Parent {
protected:
    B _b;
    auto get_AorB() -> std::variant<A, B> override {
        return _b;
    }
};

int main() {
    std::cout << "Hello World";
    auto c = Child();
    c.doit(); // "this is B!"
}
Eljay
  • 4,648
  • 3
  • 16
  • 27