Reputation: 1114
I read that you can extend a structure by using inheritance. I have a derived class in which I want to have the same struct that its parent but extended with more fields. This seems to work, however when I call a method from the Parent class that modified the structure, it does not have effect in the Child structure attribute. Here, an example of what am I trying:
class Parent
{
public:
struct info
{
int a;
};
info data;
virtual void main(void);
};
void Parent::main()
{
data.a =1;
}
class Child: public Parent
{
public:
struct info2: public info
{
int b;
};
info2 data;
virtual void main(void);
};
void Child::main(void)
{
Parent::main();
data.b = 2;
std::cout << data.a << "\n";
std::cout << data.b << "\n";
}
int main(void)
{
Parent *base;
Child derived;
base = &derived;
base->main();
return 0;
}
This instead of printing 1 and 2
prints 0 and 2
. So basically as if the attribute data from the derived class is not modified by the call to Parent::main
.
What the right way of doing that? Am I completely doing it wrong?
Upvotes: 1
Views: 1405
Reputation: 4339
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
Upvotes: 1
Reputation: 164
You have to use the below code:
void Child::main(void)
{
Parent::main();
data.b = 2;
std::cout << Parent::data.a << "\n";
std::cout << data.b << "\n";
}
Upvotes: 0
Reputation: 311196
You mean
void Child::main(void)
{
Parent::main();
data.b = 2;
std::cout << Parent::data.a << "\n";
std::cout << data.b << "\n";
}
The name data
declared in the derived class hides the name data
declared in the base class. So you need to use a qualified name to access a hidden member of the parent class.
As for data member a
of the member data of the derived class then it was not initialized.
Objects of the derived class have two data members data
: one is inherited with the type info (which name is hidden in the derived class) and other is the own data member of the derived class.
The base class knows nothing about the data member data
of the derived class.
You could define a virtual function within the class info. For example
#include <iostream>
class Parent
{
public:
struct info
{
int a;
virtual void set( int a )
{
this->a = a;
}
};
info data;
virtual void main(void);
};
void Parent::main()
{
data.set( 1 );
}
class Child: public Parent
{
public:
struct info2: public info
{
int b;
void set( int a ) override
{
this->a = a;
}
};
info2 data;
virtual void main(void);
};
void Child::main(void)
{
data.set( 3 );
data.b = 2;
std::cout << data.a << "\n";
std::cout << data.b << "\n";
}
int main(void)
{
Parent *base;
Child derived;
base = &derived;
base->main();
return 0;
}
The program output is
3
2
Upvotes: 1