2013-06-01 2 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; 
(210)

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

이 프로그램을 실행하면 당신은 그것이 작동하지 않는 것을 볼 것입니다. 몬스터의 건강은 감소해야하지만, 플레이어의 건강은 초기 값에 머물러 있습니다. 내가 생각하는 이유는 이것이다. 플레이어에는 정적 데이터 만 있습니다 (PlayerImpl _impl에 캡슐화 됨). 이것은 코드에서 다른 함수에서 호출 할 수있는 하나의 전역 Player 객체를 가질 수 있습니다. (monostate 패턴.)하지만 기본 클래스 인 Combat는 동적입니다. 그래서 플레이어 플레이어를 만들 때마다 그 일이 일어나고 있습니다. in fight() 실제로 Combat :: _ health가 기본값 인 새로운 Combat을 얻고 있습니다. 플레이어가 범위를 벗어나면 _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); 

난 당신이 프레임을 구현하기 위해 노력하고 있기 때문이다 용의자하지 않은 매개 변수없이 전역 함수를 호출하는 원인이 대신 전화입니다 아직 프레임을 캡슐화했기 때문에 프레임은 참가자가 누구인지 알지 못하며 전역을 사용하여 참가자를 강요합니다.

원본을 다시 한 번 살펴보고 정량 사용으로 인해 플레이어의 클래스를 구체적으로 파악하고 캡슐화를 해체하는 방법에 대해 알아 봅니다.

싱글 톤이나 전역을 피해야 만하는 것은 아닙니다. 자신을 확인하십시오.이 정보가 "PrawnShriveller"및 "PrawnShriveller"를 비롯한 모든 클래스에서 볼 수 있으며 수정할 수 있다는 것을 실제로 말 했나요? "MP3Player"뿐만 아니라 전역 함수 "WhenIdleFormatHardDriveCatchFireOrDoOtherThings()"?