1

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?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
edgarstack
  • 1,096
  • 1
  • 10
  • 18

3 Answers3

1

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
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Can't I make the data.a from `Child` be the same than the Parent one? Somehow I want that the child has data.a filled by the parent and its contained in the `Childs` struct. – edgarstack Sep 18 '19 at 17:24
  • @edgarstack In the parent class you did not initialize the data member a. The base class knows nothing about the new structure info2 of the derived class. – Vlad from Moscow Sep 18 '19 at 17:24
  • Well, I want the `Parent::main()` method to modify `Child::data.a`. Is there a way to achieve that? – edgarstack Sep 18 '19 at 17:26
  • @edgarstack No there is no way. Base classes know nothing about which new data members their derived classes have. – Vlad from Moscow Sep 18 '19 at 17:29
  • So maybe I am approaching this from the wrong angle. Is there a better/more correct way of doing what I am trying to do? So basically: Parent class has a struct that is modified by its methods. Then the Child wants to add some new information unique for the child in the same structure. However, I want to combine (and reuse) calls to Parent's methods with Child methods to fill the structure. – edgarstack Sep 18 '19 at 17:33
  • In this case, @edgarstack, you would most likely need to use `data` polymorphically, too. – Justin Time - Reinstate Monica Sep 18 '19 at 17:40
  • Any pointers on how to use `data` polymorphically? I know how to do it with class methods, but not with struct members. – edgarstack Sep 18 '19 at 17:42
  • Thanks for the example. The only problem is that you had to call the `Set(3)` in the Child, but what I want to do is `Parent::main()` to do the set. In the example I posted here the functions are just a mock, in my real case the Parent::main() is doing a lot of things until it knows what to set as a value to all the members (there are many more too). So I just wanted to avoid copying the Parents::main to the Child, cuz a solution would be to duplicate the code, but is not elegant nor robust. – edgarstack Sep 18 '19 at 17:50
1

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

  • amazing! thats what I wanted. Is there any place I can learn these things properly? I don't think I will find this in a standard C++ book. – edgarstack Sep 19 '19 at 09:07
  • 1
    I'm sure there are books that teach as least some of this stuff, @edgarstack; I'd be surprised if the Rule of Three/Five/Zero isn't discussed anywhere. Apart from that, CPPReference.com is a _mostly_ accurate, slightly easier-to-read reference for the C++ standard, and CPlusPlus.com is easier to read than it (albeit somewhat out of date). There are tutorials online on sites like CProgramming.com, and a lot of good explanations on here (if you can find them). A lot of times, though, experimentation will be your best teacher, and a lot of complex tricks like this are just knowing how to use... – Justin Time - Reinstate Monica Sep 19 '19 at 14:29
  • 1
    ...simple things you're more likely to find explained elsewhere. (In this case, for example, I'm just applying the same principles you're learning about, inheritance & polymorphism. Accessing `data`'s fields through a pointer here works similarly to `base->main()` dynamically choosing the correct version of `main()` to call even though `base` is ` Parent*` (because every `Child` is also a `Parent`, or more mechanically contains a `Parent` for the pointer to latch onto); the main difference is that we do the dynamic dispatch work ourself instead of it being automated by `virtual` functions. – Justin Time - Reinstate Monica Sep 19 '19 at 14:52
  • 1
    (Also note that you need to be careful when doing something like downcasting `info*` to `info2*` with `static_cast`, though. Downcasting by `static_cast` doesn't do any type checking, and is thus only for when you know the actual pointed-to type is-an `info2` (where "is-a(n) `X`" means "is either `X` or derived from `X`); since `Child` explicitly provides an `info2` for `data`, we can guarantee this. You'd normally use `dynamic_cast` if you need to downcast, which would give you a `nullptr` if the object's actual type isn't actually the type you're casting it to.) – Justin Time - Reinstate Monica Sep 19 '19 at 14:59
0

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";
}
Vahid Bahramian
  • 134
  • 1
  • 7