You're entirely correct, Parent::main()
is unable to access Child::data
, and knows nothing about any mythical info2
type; to it, Parent::data
is all there is, and info
is its type.
There are a few easy ways to make Child::main()
work with Child::data
instead of Parent::data
, or to make it access the desired field from each version, but I suspect that's not what you're after. If you want both Parent
and Child
to see the same data
(as an info
and an info2
, respectively), then data
should itself be used polymorphically. For this example, I'll use a regular pointer for simplicity (and in turn, operator.
will be replaced with operator->
, when accessing data
's members), but I would recommend looking into smart pointers such as std::unique_ptr
to simplify the memory management.
class Parent
{
public:
struct info
{
int a;
// Chances are, it's going to be deleted through an info* no matter what it is. Therefore, virtual destructor.
virtual ~info() = default;
};
info* data; // Consider using a smart pointer here, like std::unique_ptr.
virtual void main(void);
virtual void output() const; // Just adding this for convenience.
// Default constructor now allows data to be supplied, or creates it if necessary.
Parent(info* dp = nullptr) : data(dp ? dp : new info) {}
// Correct destructor will always be called.
virtual ~Parent() { if(data) { delete data; } }
};
void Parent::main()
{
data->a =1;
}
We now remove the field Child::data
, and instead have Child
supply its desired data
to Parent
's constructor.
class Child: public Parent
{
public:
struct info2: public info
{
int b;
};
//info2 data;
virtual void main(void);
void output() const override; // Just adding this for convenience.
Child() : Parent(new info2) {}
};
Child
will, when required, view data
as an info2
instead of an info
.
void Child::main(void)
{
Parent::main();
auto dataPtr = static_cast<info2*>(data); // In Child, we know data is an info2*.
dataPtr->b = 2;
// Just gonna move these to output(), for a cleaner illustration.
//std::cout << "Data->a: " << data->a << "\n";
//std::cout << "Data->b: " << dataPtr->b << "\n";
}
This will then cause data
to work as desired, with Parent
and Child
both having the correct type.
void Parent::output() const {
std::cout << "Parent:\n";
std::cout << "> Data->a: " << data->a << "\n";
}
void Child::output() const /*override*/ {
std::cout << "Child as ";
Parent::output();
auto dataPtr = static_cast<info2*>(data);
std::cout << "Child:\n";
std::cout << "> Data->a: " << dataPtr->a << "\n";
std::cout << "> Data->b: " << dataPtr->b << "\n";
}
This will then perform as expected, as seen live on Coliru. Note that if you want to be able to, e.g., create a Child
from a pre-existing Parent
, you'll want to add a move constructor that can make an info2
from an info
; you should consider following the Rule of Five, or using a smart pointer instead of a raw pointer. ;P