Hillsy7
Hillsy7

Reputation: 43

C++ Creating Child Class from a Parent Class that's already been initialised

I have a class "Player". Its members are simple strings and ints and I've got Getters and Setters for each of these...basic stuff: (there's a load of members so I've just given 3 to shrink the code):

PLAYER.H

class Player
{
private:
string Name;
string Role;
    int FFDefence;
    ......etc

public:
//constructor function
Player(
string Name = "Not Stated",
string vRole = "Not Stated",
    int vFFDefence = 0,
    ......etc
    )


//Getter Functions
string GetName() const;
string GetRole() const;
    int GetFFDefence() const;
    .....etc

//Setter Functions
void SetName (string x);
void SetRole(string x);
    void SetFFDefence(int x);
    ......etc
    };

PLAYER.CPP

Player::Player( string vName,
string vRole,
int vFFDefence,
......etc
{
Name = vName;
Role = vRole;
FFDefence = vFFDefence,
......etc
}
//getter functions
string Player::GetName() const {return Name; };
string Player::GetRole() const {return Role; };
int Player::GetFFDefence() const {return FFDefence; };
.....etc
//Setter Functions
void Player::SetName(string x) { Name = x ; };
void Player::SetRole(string x) { Role = x ; };
void Player::SetFFDefence(int x) { FFDefence = x ; };
......etc

So yeah - pretty bog standard......now I have a second class where one of the member functions is a Player Class itself.

BATTER.H

class Batter
{
private:
Player ID;
int Touch;
....etc

public:
Batter(Player vID, int vTouch = 0....etc);
//Getter Functions
string GetRole() const;
    int GetFFDefence() const;
    int GetBFDefence() const;....and so on.

OK - that's the code out of the way!!!!

So I've got it doing everything I want in terms of passing variables in and out....so I can create

Player Dave ("Dave", "Opener", 98, ....etc)

then later on (when I need it) create

Batter OnStrike (Dave, 10, .....etc)

All gravy....OK so I've started looking into inheritance and realized this is what I should be doing....back converting not a problem (did this with arrays and vectors the other day)...

Here's my problem:

With what I've got now, I can create "Player Dave" and then pass him into the subclass of Batter whenever I need to. How do I do the same with traditional inheritance? How do I take a specific instance (already created) of Player and use that as the parent for a specific instance of the child class Batter? As far as I can deduce at the moment, you need to create both at the same time.

Upvotes: 3

Views: 28423

Answers (5)

Pete
Pete

Reputation: 4812

It really depends how you actually want your code factored. Will a given Player ever become anything other than a Batter? If they can, then it is probably best to use aggregation (in a similar way to how you do now).

If you are aggregating then maybe use another class to hold the data. You could have a PlayerInfo class or struct and aggregate that:

struct PlayerInfo
{
    string role_;
    int ff_defence_;
    ...
};

class Player 
{
public:
    Player(PlayerInfo const& info)
        : info_(info)
    {}
    virtual ~Player() = 0;
    virtual void doSomething();

    PlayerInfo const& getPlayerInfo() const { return info_; }
private:
    PlayerInfo info_;
};

class Batter : public Player
{
public:
    Batter(PlayerInfo const& info)
        : Player(info)
    {}
    virtual void doSomething();
};

If you actually want the inheritance then other answers here tell you what you need to do - construct an instance of Batter and pass on the constructor arguments to a constructor of the class you derive from (e.g. Batter) to initialize it.

Think carefully about what are you trying to express in your code.

The reason you would want to have Batter derived from Player is if you need virtual functions in Player that are implemented in Batter and do something different depending upon whether or not it is a Player or a Batter.

As an aside, its best to keep base classes abstract if possible, so Player would never be instantiated directly and would always need to be derived. I'd recommend reading Scott Meyers 'More Effective C++' to understand why this is. There's a section in there devoted to that. In fact some of the finer points of inheritance and OO design in general are nicely explained.

What you may actually want is something slightly different depending upon where you anticipate your model to change, and additionally where you you need it to have the dynamic behaviour possible through the use of virtual functions?

You could have a Player class that has all your player specific details. Then you could have a PlayerBehaviour class that implements what the player does:

class Player;

class PlayerBehaviour
{
public:
    virtual ~PlayerBehaviour() = 0;
    virtual void doSomething(Player* player) = 0;
};
inline PlayerBehaviour::~PlayerBehaviour() {}

class BatterBehaviour : public PlayerBehaviour
{
public:
    virtual void doSomething(Player* player) {
        if (player->isAngry()) {
            throwBatOnFloor();
        }
    }
    void throwBatOnFloor();
};

class Player {
public:
    Player(...stuff...);

    void doSomething() {
        if (behaviour_.get()) {
            behaviour_->doSomething(this);
        }
    }
private:
    auto_ptr<PlayerBehaviour> behaviour_;
    // Due to the auto_ptr, the default copy and assignment operators are
    // dangerous.  You could use a smart pointer or implement 
    // these by having a clone() function in the behaviour class.
    // Therefore copy/assign are private to prevent accidental misuse.
    Player(Player const&);
    Player& operator=(Player const&);
}; 

So, inheriting Batter from Player models the situation as a Batter is-a Player. Having a Behaviour models the situation as a Player has-a Behaviour such as a Batter.

Upvotes: 0

Tony Hopkinson
Tony Hopkinson

Reputation: 20320

You are doing aggregation here, not inheritance. A Batter has a player. Inheritance would be a batter is a player.

Your design is good, you don't want to do inheritance for this.

While it's okay to say a Batter is always a Player from a conceptual point of view in this case, when you are dealing with a Batter, much of what player describes is irrelevant and when dealing with them as a player, they may not be batting.

Baseball is a bit foreign to me, but if you went down the inheritance route, you'd have descendants of player for each role in the team and get in a right mess when your pitcher came out to bat.

A classic illustration of the inheritance route.

Is

Animal -> Fliers -> Bird -> Merlin
       -> Runners -> Rodent -> Gerbil

Where do you put Bat and Ostrich?

You are left with saying a Bat is a bird, inventing a new class FlyingRodent, or Rodent having two parents...

All of which will lead to a confusing bug fest.

View all unconscious reaches for the inheritance hammer with extreme suspicion.

Upvotes: 0

Jonathan Wakely
Jonathan Wakely

Reputation: 171263

Stop using the "parent" and "child" terminology, think of "base" classes and "derived" classes ... that's what everyone else calls them. "Parent" and "child" can be used in too many other ways (e.g. an object that owns another one) so it's confusing terminology if you're talking about an inheritance relationship.

The derived class contains an entire instance of the base type inside itself. When the derived constructor starts executing the first thing it does is construct all its bases, which it does by calling their constructors. So the derived class can control how the base is constructed by passing it the right arguments:

class Base {
public:
  Base(std::string nm) : name(nm) { }

protected:
  std::string name;
};

class Derived : public Base {
public:
  // construct my base by passing name to it
  Derived(std::string name, int ii) : Base(name), i(ii) { }

private:
  int i;
};

Derived d("Dave Derived", 1);

This creates both the Base and Derived objects at the same time (one inside the other) which is probably what you want.

If do have an existing Base object and you want the base part of the derived object to be the same as that other one then you can pass it an object to copy:

class Base {
public:
  Base(std::string nm) : name(nm) { }
protected:
  std::string name;
};

class Derived : public Base {
public:
  // construct my base by passing name to it
  Derived(std::string name, int ii) : Base(name), i(ii) { }

  // construct my base by passing another Base to it:
  Derived(const Base& b, int ii) : Base(b), i(ii) { }

private:
  int i;
};

Base b("Barry Base");
Derived d(b, 2);

This doesn't put the existing Base object, b, inside the Derived one, instead it makes the base object a copy of the object b, by calling the Base copy constructor, so now there are two Base objects, the original b and the one inside d. This is closer to your original code, where the Batter contains a Player member, but now it's a base class not a member.

If you do want to use inheritance, the first form is probably more appropriate, where you pass arguments to the derived class and it uses those arguments to create the base.

Upvotes: -1

Wug
Wug

Reputation: 13196

The idea with polymorphism is that if you have some class:

class Batter : public Player

Then every batter is also a player. So, for example, if you had a batter called dave, you'd be able to use dave wherever a Player was expected. You could for example:

int FunctionThatDoesSomething(Player &p, string some_parameter, ...);

...

FunctionThatDoesSomething(dave, "foo", ...);

Be careful to avoid slicing, which is when you accidentally make a base class copy of a subclass (this does not preserve subclass specific state. If you need to pass dave around, make sure you only refer to dave, don't copy dave. dave doesn't like to be copied.)

How exactly you build your players and batters is up to you. For example, your might have constructors with these signatures:

Player::Player(string name, string role, int vFFDefense);
Batter::Batter(Player &p, int vTouch, int moreStats);

Under some circumstances this might be convenient, but it's not particularly efficient because you have to create and copy the base class (not that efficiency is a big deal for small classes like this, but there's no point in trying to do things the dumb way). You would be better off making a constructor that takes everything it needs, and uses subobject initialization:

Batter::Batter(string name, string role, int vFFDefense, int moreBaseStats, int vTouch, int moreStats) : Player(name, role, vFFDefense, moreBaseStats)
{
    ...

But your implementation is ultimately up to you.

Upvotes: 1

celtschk
celtschk

Reputation: 19721

Just initialize your base object with the object provided:

class Player
{
  Player(Player const&); // copy constructor (might be implicitly generated)
  ...
};

class Batter:
  public Player
{
  Batter(Player const& p, other arguments):
    Player(p),
    ...
  {
    ...
  }
};

On the other hand, there's the question whether inheritance of Batter from Player is the right tool in your case. The fact that you pass a Player object to construction hints at the fact that a Player may become a batter, and maybe later also stop being a batter. That is, Batter is actually a role which the player may temporarily have. Therefore it may be a better idea to separate the Player object from the role, by having a separate Role hierarchy where Batter and Pitcher derive from Role, and Player has a method which returns the current role, and another which can assign another role to the player.

Upvotes: 3

Related Questions