3

我有一个班级“球员”。它的成员是简单的字符串和整数,我为其中的每一个都有 Getter 和 Setter ......基本的东西:(有很多成员,所以我只给了 3 个来缩小代码):

播放器.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
    };

播放器.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 的特定实例的父级?据我目前可以推断,您需要同时创建两者。

4

5 回答 5

3

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

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

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

另一方面,在你的情况下,继承BatterfromPlayer是否是正确的工具是个问题。您将Player对象传递给构造的事实暗示了 Player 可能成为击球手的事实,并且可能稍后也不再是击球手。也就是说,Batter实际上是玩家可能暂时拥有的角色。因此,将Player对象与角色分开可能是一个更好的主意,方法是拥有一个单独的Role层次结构 whereBatterPitcher派生自Role,并且Player有一个返回当前角色的方法,以及另一个可以将另一个角色分配给玩家的方法。

于 2013-06-18T22:05:33.587 回答
1

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

class Batter : public Player

那么每个击球手也是一个球员。因此,例如,如果您有一个名为 的击球手dave,您就可以在dave任何需要玩家的地方使用。例如,您可以:

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

...

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

小心避免切片,这是当您不小心制作了子类的基类副本时(这不会保留子类特定的状态。如果您需要传递dave,请确保您只引用dave不要复制dave. dave不喜欢被复制。)

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

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

在某些情况下,这可能很方便,但并不是特别有效,因为您必须创建和复制基类(对于像这样的小类来说,效率并不是什么大问题,但是尝试以愚蠢的方式做事是没有意义的) . 您最好创建一个构造函数来获取所需的一切,并使用子对象初始化:

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

但是您的实施最终取决于您。

于 2013-06-18T22:13:32.817 回答
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并将构造函数参数传递给你从(例如)派生的类的构造函数Batter来初始化它。

仔细想想你想在你的代码中表达什么。

您想要从中Batter派生的原因是您是否需要在其中实现的Player虚函数并根据它是 a还是 a来做不同的事情。PlayerBatterPlayerBatter

顺便说一句,如果可能的话,最好保持基类抽象,因此Player永远不会直接实例化并且总是需要派生。我建议阅读 Scott Meyers 的“更有效的 C++”来理解为什么会这样。里面有一节专门讲这个。事实上,继承和 OO 设计的一些更精细的点一般都得到了很好的解释。

您可能真正想要的是稍微不同的东西,具体取决于您预计模型将在哪里更改,以及您需要它在哪里通过使用虚函数来获得可能的动态行为?

您可以拥有一个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模型继承情况为 a Batteris-a Player。拥有 aBehaviour将情况建模为Playerhas-a Behaviour,例如 a Batter

于 2013-06-18T22:36:50.820 回答
0

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

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

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

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

继承路线的经典例证。

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

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

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

所有这些都会导致令人困惑的错误盛宴。

以极度怀疑的眼光看待所有无意识伸手去拿传承锤的人。

于 2013-06-18T22:43:25.757 回答
-1

停止使用“父”和“子”术语,想想“基”类和“派生”类……其他人都这么称呼它们。“父”和“子”可以以太多其他方式使用(例如拥有另一个对象的对象),因此如果您谈论的是继承关系,术语会令人困惑。

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

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

这会同时创建 theBaseDerived对象(一个在另一个中),这可能是您想要的。

如果确实有一个现有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对象,原始对象b和内部对象d。这更接近您的原始代码,其中Batter包含一个Player成员,但现在它是一个基类而不是成员。

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

于 2013-06-18T22:05:43.063 回答