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

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

本文关键字:父类 创建 子类 初始化 C++      更新时间:2023-10-16

我有一个类"Player"。它的成员是简单的字符串和整型我为每一个都有getter和setter。基本的东西:(有很多成员,所以我只给了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的子类。我如何对传统继承做同样的事情?我如何采取一个特定的实例(已经创建)的播放器,并使用它作为父类的子类糊的特定实例?据我目前的推断,你需要同时创建这两个。

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

class Player
{
  Player(Player const&); // copy constructor (might be implicitly generated)
  ...
};
class Batter:
  public Player
{
  Batter(Player const& p, other arguments):
    Player(p),
    ...
  {
    ...
  }
};

另一方面,有一个问题,从Player继承Batter是否是您的情况下的正确工具。你将一个Player对象传递给建筑的事实暗示了这样一个事实,即玩家可能会变成击球手,之后也可能不再是击球手。也就是说,Batter实际上是玩家可能暂时拥有的角色。因此,将Player对象与角色分开可能是一个更好的主意,通过拥有单独的Role层次结构,其中BatterPitcher派生自Role, Player有一个返回当前角色的方法,另一个可以将另一个角色分配给玩家。

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

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

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

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

但是你的实现最终取决于你自己。

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

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

在这种情况下,从概念的角度来看,击球手总是一个球员是可以接受的,但当你与击球手打交道时,球员所描述的大部分内容都是无关的,当你以球员的身份处理他们时,他们可能没有击球。

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

继承路径的经典示例。

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

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

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

所有这些都将导致一个令人困惑的bug测试。

以极度怀疑的态度查看所有无意识的继承之锤。

这实际上取决于您希望如何分解代码。一个给定的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)来初始化它。

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

您想要从Player派生Batter的原因是,如果您需要在Player中实现在Batter中的虚函数,并根据它是Player还是Batter做一些不同的事情。

顺便说一句,最好尽可能保持基类是抽象的,这样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&);
}; 

因此,从Player继承Batter将情况建模为Batter is-a Player。有一个Behaviour模型的情况下,Player有一个Behaviour,如Batter .

停止使用"父"answers"子"的术语,想想"基"类和"派生"类…其他人都这么叫他们。"Parent"answers"child"可以在很多其他方式中使用(例如,一个对象拥有另一个对象),所以如果你谈论继承关系,这是一个令人困惑的术语。

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

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对象中,相反,它通过调用Base复制构造函数使基对象成为b对象的副本,所以现在有两个Base对象,原始的bd中的一个。这更接近原始代码,其中Batter包含Player成员,但现在它是一个基类而不是成员。

如果你想使用继承,第一种形式可能更合适,你传递参数到派生类,它使用这些参数创建基类。