-1

I have a class Child that inherits from a class Parent. They both have a private instance variable named a. The parent class has a method to change the a variable.

Parent.h

#pragma once
#include <iostream>
class Parent {
public:
    Parent() {}
    ~Parent() {}

    virtual void changeA() {
        a++;
        std::cout << "Parent a: " << a << std::endl;
    }

private:
    int a = 0;
};

Child.h

#pragma once

#include "Parent.h"

class Child : public Parent {
public:
    Child() {}
    ~Child() {}

    void changeA() {
        Parent::changeA();
        std::cout << "Child a: " << a << std::endl;
    }

private:
    int a = 0;
};

main.cpp

#include "Child.h"
#include "Parent.h"
#include <iostream>

int main() {
    Parent parent = Parent();
    Child child = Child();

    parent.changeA();
    std::cout << "---" << std::endl;
    child.changeA();

    return 0;
}

stdout

Parent a: 1
---
Parent a: 1
Child a: 0

It seems as if the Child object also has a copy of the Parent object. How do I get the Parent's changeA method to change the Child's a instance variable?

Clarification: I need this because I have a parent class Entity with two children Enemy and Player, and both classes need to use the same init method inherited from Entity.

Thuong Vo
  • 77
  • 11
  • You are explicitly calling the `Parent::changeA();` What do you expect? – Dmitry Kuzminov Dec 25 '20 at 05:24
  • 3
    Question: You want to have both `Parent::a` and `Child::a`? This is quite unusual. Anyway, `Parent::changeA` can't do the job because there's no way for `Parent` to know that `Child` even exists (But look up CRTP) or that it's not really a `Child2`, but if you make it `virtual` you could have `Child::changeA` do the work. – user4581301 Dec 25 '20 at 05:25
  • Side note: When a construct tor or destructor doesn't do anything, leave it out or explicitly default it. The compiler will autogenerate the correct code. – user4581301 Dec 25 '20 at 05:30
  • @user4581301, that is not quite true. The parent class *can* know of the child like it does in CRTP. – Dmitry Kuzminov Dec 25 '20 at 05:33
  • I don't see the code that displays `Parent a`, did you lose it in the copy/paste? – Mark Ransom Dec 25 '20 at 05:42
  • @MarkRansom Thanks. The question has been edited. – Thuong Vo Dec 25 '20 at 05:45
  • @user4581301 How would I do that? – Thuong Vo Dec 25 '20 at 05:47
  • @DmitryKuzminov I'm not seeing where you are coming from. You could forward-declare `Child`, but you're not gaining much. If I'm missing something, please fill me in. – user4581301 Dec 25 '20 at 05:52
  • If possible, get rid of the `init` method and make smarter constructors. The `Parent` constructor will be called as part of the `Child` constructor and make the problem go away. – user4581301 Dec 25 '20 at 06:07

2 Answers2

2

The parent class knows nothing of any child. It can define a virtual method, and the child can override this method like that:

class Child : public Parent {
    void changeA() override {
        // Here the Child::a will be changed:
        ++a;
        std::cout << "Child a: " << a << std::endl;
    }

private:
    int a = 0;
};

The override (or final) is an optional specifier, but it is the best practice to specify one.

There is however a way to let the parent know the child. That is called Curiously Recurring Template Pattern (CRTP):

template<typename Child>
class Parent {
public:
    virtual void changeA() {
        // Here the Child::a is changed:
        static_cast<Child*>(this)->a++;
        std::cout << "Parent a: " << a << std::endl;
    }

private:
    int a = 0;
};

class Child : public Parent<Child> {
public:
    void changeA() override {
        Parent::changeA();
        std::cout << "Child a: " << a << std::endl;
    }

private:
    int a = 0;
};

The code above would not compile however because the Child::a is private, and the Parent<Child> has no access to this member. You may either make it public or make the Parent a friend:

class Child : public Parent<Child> {
public:
    void changeA() override {
        Parent::changeA();
        std::cout << "Child a: " << a << std::endl;
    }

private:
    int a = 0;
    friend class Parent<Child>;
};

Overall I don't understand your intention, and the code is error prone. I guess there are better ways to solve your actual problem.

Update: another way to achieve the behavior is to store a reference in the Parent. You don't even need to make the method virtual in this case:

class Parent {
public:
    Parent(int &a) : a(a) {}
    void changeA() {
        a++;
        std::cout << "Parent a: " << a << std::endl;
    }

private:
    int &a;
};

class Child : public Parent {
    Child() : Parent(a) {}

private:
    int a = 0;
};

In this case only one instance of the integer field is allocated, and the Parent uses the same data the Child stores.

Dmitry Kuzminov
  • 6,180
  • 6
  • 18
  • 40
0

Make Parent::changeA a virtual function and add code to Child::changeA to modify Child::a. Quick example:

#include <iostream>

class Parent {
public:
    virtual ~Parent() {} // almost always want a virtual destructor
                         // Don't need it here, but if you find yourself 
                         // having to delete a Child through a Parent 
                         // pointer this is a must.

    virtual void changeA() { // virtual function at runtime the derived version, 
                             // if one exists, will be called instead of 
                             // Parent::changeA
        a++;
        std::cout << "Parent a: " << a << std::endl;
    }

private:
    int a = 0;
};


class Child : public Parent {
public:

    void changeA() override { // will be called in place of Parent::changeA 
                              // the override keyword will cause a compiler 
                              // error if no override takes place. Zero 
                              // runtime cost and saves debugging. Winning.
        Parent::changeA(); // modifies Parent::a
        a++; // modifies its Child::a
        std::cout << "Child a: " << a << std::endl;
    }

private:
    int a = 0;
};

int main() {
    Parent parent;
    Child child;

    parent.changeA();
    std::cout << "---" << std::endl;
    child.changeA();

    return 0;
}

Documentation for override.

user4581301
  • 33,082
  • 7
  • 33
  • 54
  • I don't want to rewrite the parent method's code in the child as well. – Thuong Vo Dec 25 '20 at 06:06
  • 1
    @ThuongVo you haven't repeated. They are different `a`s and both need to be set. What you really need to ask though, is why do you have duplicated members? – user4581301 Dec 25 '20 at 06:14
  • The parent class is never really used, but it contains the shared code between its child classes. I want to share a method between child classes, and have one place to change that code. To get it to compile, I had to make duplicate members. The problems is that the code inherited doesn't change the child members. – Thuong Vo Dec 25 '20 at 06:56
  • You should explore that duplication. Odds are very good that you have what's called an [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) You're may be asking a question about a problem with a solution when you should be asking about the problem the solution is failing to solve effectively. – user4581301 Dec 25 '20 at 07:01
  • So how do I share the code? After I got rid of the init method, the code still can't be shared between children because it seems that there is an instance of the parent object for each instance of the child object. The constructor code is modifying the variables of the parent instance for the child instance and not modifying the child instance's variables. – Thuong Vo Dec 25 '20 at 07:16
  • @ThuongVo, from what I can see, you do not need inheritance, but you need a template. That is a way to "share a method between child classes". – Dmitry Kuzminov Dec 25 '20 at 07:19
  • @DmitryKuzminov Thank you for your answer. It was exactly what I needed. What would inheritance be useful for, however? – Thuong Vo Dec 25 '20 at 07:26
  • @ThuongVo, that is a long story. It is not possible to answer what would be the hammer useful for (because there are many different usage scenarios), but in many cases it is possible to answer when is shouldn't be used (e.g. where a saw should be used). – Dmitry Kuzminov Dec 25 '20 at 07:29