2013-06-01 38 views
0

考虑以下类别。 (从一个游戏,但极大地简化了。)是否可以使用静态构造函数初始化程序?

combat.h:

class Combat { 
public: 
    Combat(); 
    Combat(int health, int offense, int defense); 
    virtual ~Combat(); 
    int attack(); 
    int defend(); 
    int health() const; 
    void setHealth(int health); 

private: 
    struct CombatImpl; 
    std::unique_ptr<CombatImpl> _impl; 
}; 

combat.cc:

struct Combat::CombatImpl { 
CombatImpl(); 
    CombatImpl(int health, int offense, int defense); 
    ~CombatImpl()=default; 

    int   _health; 
    int   _offense; 
    int   _defense; 
}; 

Combat::Combat(int health, int offense, int defense) : 
    _impl { new Combat::CombatImpl(health, offense, defense) } { 
} 

Combat::~Combat()=default; 

int Combat::attack() { 
    int hits = 0; 

    for(int i = 0; i < _impl->_offense; i++) { 
     if (rand() % 6 == 5) { 
      hits++; 
     } 
    } 

    return hits; 
} 

int Combat::defend() { 
    int parries = 0; 

    for(int i = 0; i < _impl->_defense; i++) { 
     if (rand() % 6 == 5) { 
      parries++; 
     } 
    } 

    return parries; 
} 

int Combat::health() const { 
    return _impl->_health; 
} 

void Combat::setHealth(int health) { 
    _impl->_health += health; 
} 

Combat::CombatImpl::CombatImpl(int health, int offense, int defense) { 
    _health = health; 
    _offense = offense; 
    _defense = defense; 
} 

monster.h:

class Monster: public Combat { 
public: 
    Monster(int health, int offense, int defense); 
    virtual ~Monster(); 
} 

monster.cc:

Monster::Monster(int health, int offense, int defense) 
    : Combat(health, offense, defense) {} 

Monster::~Monster()=default; 

player.h:

class Player : public Combat { 
public: 
    Player(); 
    virtual ~Player(); 

private: 
    struct PlayerImpl; 
    static PlayerImpl _impl; 
}; 

player.cc:

struct Player::PlayerImpl { 
    PlayerImpl()=default; 
    ~PlayerImpl()=default; 
} Player::_impl; 

Player::Player() : Combat(17, 1, 1) { 
} 

Player::~Player()=default; 

...最后,测试程序使用它们:

#include <cstdio> 
#include <cstdlib> 
#include <ctime> 
#include <memory> 
using namespace std; 
#include "monster.h" 
#include "player.h" 

static Monster monster(3, 1, 1); 

void fight() { 
    Player player; 
    int damage = monster.attack(); 
    damage -= player.defend(); 

    if (damage > 0) { 
     player.setHealth(-damage); 
    } 

    if (player.health() < 1) { 
     return; 
    } 

    damage = player.attack(); 
    damage -= monster.defend(); 

    if (damage > 0) { 
     monster.setHealth(-damage); 
    } 

    if (monster.health() < 1) { 
     return; 
    } 

} 

int main() { 
    Player player; 

    srand(time(NULL)); 

    while (player.health() > 0 && monster.health() > 0) { 
     fight(); 

     printf("player health = %d monster health = %d\n", player.health(), 
      monster.health()); 
    } 
} 

如果你运行这个程序你会看到它不起作用。怪兽的健康状况应该会降低,但是玩家的健康状况会停留在初始值。我认为这是发生的原因是这样的; Player只有静态数据(封装在PlayerImpl _impl中)这样我就可以拥有一个全局Player对象,我可以从我的代码中的不同函数中调用它。 (单稳态模式)。但其基类Combat是动态的。所以发生的是每次我创建Player播放器;在战斗()我实际上得到一个新的战斗,其中战斗:: _健康是默认值。当玩家超出范围时,_health的任何更改都将丢失。在怪物中,这不是问题,因为怪物对象也有动态数据。理想情况下,我可以说

class Player : public static Combat { 

意思是使静态这个特定的战斗,但这是一个语法错误。有没有另外一种方法呢?还是我把自己画在一个角落?

+10

**远**代码太多。请简化这一些更多! –

+4

为什么你创造了很多玩家*对象*?一个对象(类类型)是一个类的*实例*。通过使用这种方法,创建许多玩家对象,所有玩家都应引用一名玩家,您的代码表明与您的意图/发生的事情相矛盾。将播放器对象(引用)传递给每个函数,或者使用自由/静态的'getPlayer'函数(返回'Player&')。 – dyp

+0

@OliCharlesworth对不起,关于示例代码的长度。真实情况要长得多,而且这是我认为可以削减的最低限度,并且仍然为我的问题提供了足够的背景。 – Jaldhar

回答

3

这听起来像你没有真正想过你的封装层次。来自战斗的玩家没有什么意义,你的执行混乱(和这个问题)支持了这一点。你不同意C++给我们多重继承而不是接口的事实,因为我相信你想描述的是Player有一个Combat接口。

清除这类问题的一种常见方法是使用转发器/桥/委托/特质/访问器类,在这种情况下可能是“Combatant”或“CombatHandler”或“CombatEntity” - 全部取决于你想要的方式继承来读 - 它的唯一目的是帮助你遍历封装图;在这种情况下,从一个实体到封装该类实体的争夺功能。

这些中间类不是简单的,只限于互连逻辑。不要放任何实际的功能;尽量保持所有成员const。

class Combatant { 
public: 
    Combatant() {} 
    virtual const Combat* Combat() const = 0; // so combat is technically our impl 
    virtual Combat* Combat() = 0; 
    // keep this interface light, it's primarily an accessor interface. 
    virtual bool CanFight() const { return (Combat() != nullptr); } 
    virtual bool CanFight(Combatant* opponent_) const { 
     return (opponent_ != nullptr && CanFight() && opponent_->CanFight()); 
    } 
}; 

class PassiveEntity() : Combatant { 
    ... 
    const Combat* Combat() const { return nullptr; } 
    Combat* Comat() { return nullptr; } 
} 

class Player : public Combatant { 
public: 
    virtual const Combat* Combat() const override { 
     // if you HAVE to use a static, something like this. 
     return &s_playerCombatImpl; 
    } 
    virtual Combat* Combat() override { 
     // but really it should be a member so it can be stateful. 
     return &m_combat; 
    } 
    ... 
}; 

class Monster : public Combatant { 
    ... 
}; 

class Corpse : public PassiveEntity { 
    ... 
}; 

你应该重构的第二件事是什么导致你调用全局函数没有参数,而不是调用

monster.fight(player); 
//or 
player.fight(monster); 

我怀疑这是因为你想实现帧和没有但封装,所以框架不知道谁是参与者,你通过使用全局强制它。

再看一下你的原创内容,并回顾一下静力学的使用如何强制你的手进一步向下:提升战斗细节和意识到Player类中,进一步打破你的封装。

这并不是说不惜一切代价避免单身人士或全球人士,只要确保自己检查自己 - 你是否确实表示这些信息是可见的,并且可以被任何类别修改,包括“PrawnShriveller”和“MP3Player”,以及全球功能“WhenIdleFormatHardDriveCatchFireOrDoOtherThings()”?

相关问题