C ++从已经初始化的父类创建子类

时间:2013-06-18 21:59:37

标签: c++ class inheritance

我有一个班级“玩家”。它的成员是简单的字符串和整数,我为这些中的每一个都有Getters和Setters ......基本的东西:(有一大堆成员,所以我只给了3来缩小代码):

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

所以是的 - 非常糟糕的标准......现在我有了第二个类,其中一个成员函数本身就是一个Player类。

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.

好的 - 那就是代码了!!!!

所以我已经做了我想要的所有事情,无论是传入和传出变量....所以我可以创建

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

然后(当我需要它时)创建

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

所有肉汁....好吧所以我开始研究继承并意识到这就是我应该做的事情......回转不是问题(前几天用数组和载体做了这个)...

这是我的问题:

凭借我现在所拥有的,我可以创建“Player Dave”,然后在需要时将其传递给Batter的子类。我如何对传统继承做同样的事情?如何获取Player的特定实例(已创建)并将其用作子类Batter的特定实例的父实例?据我所知,你需要在同一时间创建两者。

5 个答案:

答案 0 :(得分:3)

使用提供的对象初始化您的基础对象:

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

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

另一方面,问题是来自Batter的{​​{1}}的继承是否是您案例中的正确工具。您将Player对象传递给构造提示的事实是,玩家可能成为击球手,并且可能稍后也不再是击球手。也就是说,Player实际上是玩家可能暂时拥有的角色。因此,将Batter对象与角色分开可能是一个更好的选择,方法是在PlayerRole Batter派生Pitcher的情况下使用单独的Role层次结构, Player有一个返回当前角色的方法,另一个可以为玩家分配另一个角色。

答案 1 :(得分:1)

多态性的想法是,如果你有一些类:

class Batter : public Player

然后每个击球手也是一名球员。因此,举例来说,如果您有一个名为dave的击球手,那么无论玩家在哪里,您都可以使用dave。你可以举例如:

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

...

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

小心避免切片,这是在您不小心制作子类的基类副本时(这不保留子类特定的状态。如果您需要传递dave,请确保只有请参考dave,不要复制davedave不希望被复制。)

你如何建立你的球员和击球手取决于你。例如,您可能拥有带有这些签名的构造函数:

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

在某些情况下这可能很方便,但它并不是特别有效,因为你必须创建并复制基类(对于像这样的小类来说效率不是很大,但尝试<没有意义/ em>以愚蠢的方式做事)。你最好制作一个能够满足它所需要的一切的构造函数,并使用子对象初始化:

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

但你的实施最终取决于你。

答案 2 :(得分:0)

这实际上取决于您实际想要如何考虑代码。 给定的Player是否会成为Batter之外的其他内容?如果他们可以,那么最好使用聚合(与你现在的方式类似)。

如果您正在聚合,那么可以使用另一个类来保存数据。您可以拥有一个PlayerInfo类或结构并聚合:

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();
};

如果你真的想要继承,那么这里的其他答案告诉你你需要做什么 - 构造一个Batter的实例并将构造函数参数传递给你派生的类的构造函数(例如{{1初始化它。

仔细考虑您要在代码中表达的内容。

您希望从Batter派生Batter的原因是,您需要PlayerPlayer中实现的虚拟函数,并根据是否执行不同的操作或不是BatterPlayer

顺便说一下,如果可能的话,最好保持基类抽象,因此Batter永远不会直接实例化,并且总是需要派生。我建议阅读Scott Meyers的“更有效的C ++”来理解为什么会这样。那里有一节致力于此。实际上,很好地解释了一些更精细的继承和OO设计点。

您可能真正想要的是略有不同,具体取决于您预期模型的更改位置,以及您需要通过使用虚拟功能来实现动态行为的位置?

你可以拥有一个Player课程,其中包含所有玩家特定的详细信息。然后你可以有一个Player类来实现玩家所做的事情:

PlayerBehaviour

因此,从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&); }; 继承Batter模型的情况为Player是一个Batter。 将Player模型设为Behaviour的情况有一个Player,例如Behaviour

答案 3 :(得分:0)

你在这里进行聚合,而不是继承。击球手有一名球员。继承将是一个击球手是一个球员。

你的设计很好,你不想为此继承。

虽然可以说在这种情况下,从概念的角度来看,击球手总是一名球员,但当你与击球手打交道时,球员所描述的大部分内容都无关紧要,当他们作为一名球员与他们打交道时,他们可能会不打击。

棒球对我来说有点陌生,但是如果你沿着继承路线走下去,你就会为球队中的每个角色都有球员的后代,当你的投手出来蝙蝠时,他们就会陷入混乱。

继承路线的经典例证。

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

你把蝙蝠和鸵鸟放在哪里?

你只是说蝙蝠是一只鸟,发明了一个新类FlyingRodent,或者有两个父母的啮齿动物......

所有这些都会导致混乱的错误。

极度怀疑地查看继承锤的所有潜意识到达范围。

答案 4 :(得分:-1)

停止使用&#34;父母&#34;和孩子&#34;术语,想到&#34; base&#34;类和&#34;派生&#34;课程......这是其他人所称的。 &#34;父&#34;和孩子&#34;可以在太多其他方式中使用(例如,拥有另一个的对象),如果您正在讨论继承关系,那么它就会令人困惑。

派生类本身包含基本类型的完整实例。当派生的构造函数开始执行时,它所做的第一件事是构造它的所有基础,它通过调用它们的构造函数来完成。因此派生类可以通过传递正确的参数来控制基类的构造方式:

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);

这会同时创建BaseDerived个对象(一个在另一个内部),这可能就是你想要的。

如果确实有一个现有的Base对象,并且您希望派生对象的基本部分与另一个对象相同,那么您可以将其传递给要复制的对象:

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);

这不会将现有Base对象b放在Derived对象中,而是使基础对象成为对象b的副本,通过调用Base复制构造函数,现在有两个Base个对象,原始bd内的对象。这更靠近原始代码,其中Batter包含Player成员,但现在它是基类而不是成员。

如果您确实想要使用继承,那么第一种形式可能更合适,您可以将 arguments 传递给派生类,并使用这些参数创建基础。