-1

If I have a vector of pointers to a superclass and add members of a subclass to the vector, I can call a subclass function just fine, but as my code is right now, I can't access variables that are unique to the subclass. How do I access B from a pointer in vec?

#include <iostream>
#include <vector>

class Super {
  public:
    int A;
    Super(int a) : A(a) {}
    virtual void foo() = 0;
};

class Sub : public Super {
  public:
    int B;
    Sub(int a, int b) : Super(a), B(b) {}
    void foo() {
      std::cout << "calling foo from Sub\n";
    }   
};

int main() {
  std::vector<Super*> vec;
  vec.push_back(new Sub(2, 3));
  vec[0]->foo();                                // No problem
  std::cout << "A: " << vec[0]->A << std::endl; // No problem
  std::cout << "B: " << vec[0]->B << std::endl; // Compile Error
}
Ben Lindsay
  • 1,686
  • 4
  • 23
  • 44

2 Answers2

1

It's up to the design. The "enforced" way to do it is to cast the value to the subtype, eg:

std::cout << static_cast<Sub*>(vec[0])->B;

Now, why this is bad is because you are explicitly converting a type to another type, which defeats the purpose of abstraction and inheritance per se.

That's why in the common usage what happens is that you define a set of characteristics of a type in the superclass and then extend or implement it as you wish in the subclasses. Now this is not alway possible, like in your test case, but in principle that's why you should look after.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • 1
    It is possible. There should be a `virtual void print(std::ostream&)` (or somesuch) and a `ostream<<(ostream&, const Super&)` that invokes it. Indeed this is a textbook case for virtual dispatch. – Lightness Races in Orbit Feb 21 '16 at 23:52
  • @PreferenceBean Thanks for the additional info! I'll digest it later. – Ben Lindsay Feb 22 '16 at 00:13
  • @Jack sorry I switched the accepted answer to PreferenceBean's. Your answer addressed my immediate question, but the other one shows (what seem to be) better coding practices that accomplish the same thing – Ben Lindsay Feb 22 '16 at 01:13
1

You could fix that naively with a dynamic_cast<Sub*> (or even a static_cast<Sub*> if you could guarantee that each pointer pointed to a Sub) but … don't! This is a textbook case for virtual dispatch.

I'd add a virtual void print(std::ostream&) that's part of the interface of the entire inheritance hierarchy, as well as a operator<< that can be called on an object of any type within that hierarchy. Let the magic of C++ runtime polymorphism and function overriding do all this for you, so main doesn't need to know about the differences between A or B or what member variables each one has, nor figure out which kind of object each of your pointers actually points to.

You should also store a smart pointer to avoid that memory leak.

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

struct Super {
    int A;
    Super(int a) : A(a) {}

    virtual void foo() = 0;
    virtual void print(std::ostream& os) const
    {
        os << "A: " << A << '\n';
    };
};

struct Sub : Super {
    int B;

    Sub(int a, int b) : Super(a), B(b) {}

    void foo() { std::cout << "calling foo from Sub\n"; }
    virtual void print(std::ostream& os) const
    {
        Super::print(os);
        os << "B: " << B << '\n';
    }
};

std::ostream& operator<<(std::ostream& os, const Super& obj)
{
    obj.print(os);
    return os;
}

int main()
{
   std::vector<std::unique_ptr<Super>> vec;
   vec.emplace_back(std::make_unique<Sub>(2, 3));
   vec[0]->foo();
   std::cout << *(vec[0]);
}

(live demo)

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055