我有一个班级“玩家”。它的成员是简单的字符串和整数,我为这些中的每一个都有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的特定实例的父实例?据我所知,你需要在同一时间创建两者。
答案 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
对象与角色分开可能是一个更好的选择,方法是在Player
和Role
Batter
派生Pitcher
的情况下使用单独的Role
层次结构, Player
有一个返回当前角色的方法,另一个可以为玩家分配另一个角色。
答案 1 :(得分: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);
在某些情况下这可能很方便,但它并不是特别有效,因为你必须创建并复制基类(对于像这样的小类来说效率不是很大,但尝试<没有意义/ 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
的原因是,您需要Player
中Player
中实现的虚拟函数,并根据是否执行不同的操作或不是Batter
或Player
。
顺便说一下,如果可能的话,最好保持基类抽象,因此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);
这会同时创建Base
和Derived
个对象(一个在另一个内部),这可能就是你想要的。
如果确实有一个现有的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
成员,但现在它是基类而不是成员。
如果您确实想要使用继承,那么第一种形式可能更合适,您可以将 arguments 传递给派生类,并使用这些参数创建基础。