2

と属性を持つというクラスWarriorがあります。コンストラクターがパラメーターを受け取るようにします。uint m_healthuint m_maxHealthWarrior(uint health, uint maxHealth)

今、私はC++をたくさん勉強し、すべての構文などを知っていますが、それらの使用方法などに関するチュートリアルを見つけるのは難しいので、どのように定義すればよいのかhealth、明らかmaxHealthに:/healthmaxHealth

私が考えたいくつかの方法を次に示します。

// method 1
Warrior::Warrior(uint health, uint maxHealth) :
    m_health((health > maxHealth) ? maxHealth : health),
    m_maxHealth(maxHealth)
{}

// method 2
Warrior::Warrior(uint health, uint maxHealth) :
    m_maxHealth(maxHealth)
{
    if (health > maxHealth) {
        m_health = maxHealth;
    }
    else {
        m_health = health;
    }
}

きっと他の方法もあると思います。これが単なる意見の質問である場合は申し訳ありませんが、C ++で「推奨される」方法があるとしたら、それは何でしょうか?

4

6 に答える 6

4

ゲーム中にヘルスを設定したい場合は、次のようなことを検討してください。

Warrior::Warrior(unsigned int health, unsigned int maxHealth) :
    m_maxHealth(maxHealth)
{
    setHealth(health);
}

void Warrior::setHealth(unsigned int health)
{
    m_health = std::min(health, m_maxHealth);
}
于 2013-05-14T13:43:49.370 に答える
3

これは、実際の実装よりもインターフェイスの設計に関する問題です。コード内の両方の実装は類似しており、引数の任意の組み合わせを可能にする幅広いインターフェイスを備えた設計を実装しています。

別のアプローチは、コンストラクターで狭いコントラクトを使用することです。この場合、オブジェクトの動作は、明らかな制約health < maxHealthが true である場合にのみ明確に定義されます。その場合、コンストラクターをそのように文書化して実装します。

Warrior::Warrior(unsigned int health, unsigned int maxHealth)
  : m_health(health), m_maxHealth(maxHealth)
{
   assert(health <= maxHealth);
}

私の意見では、どちらがより良いデザインです。コンストラクターへの間違った引数を受け入れる正当な理由はありません。そこに追加することassertで、開発サイクルの早い段階で型が契約外で使用されていることを検出できます。これは、同じ型の複数の引数を持つ関数では特に重要です。誤って引数を順不同で渡したかどうかを検出するのに役立つからです。そこで広いコントラクトを使用するとmaxHealth、 が現在の と同じになることが少なくなり、後でが思ったよりも弱いhealth理由を突き止めようとして問題が発生することになります...狭いコントラクトが教えてくれます。引数はすぐに順不同です。Warrior

于 2013-05-14T13:54:15.363 に答える
3

より良い定義に応じて、「より良い」方法があります。(そして、ここでは「短い」という意味だと思います)

Warrior::Warrior(unsigned int health, unsigned int maxHealth) :
    m_health(std::min(health, maxHealth)),
    m_maxHealth(maxHealth)
{
}
于 2013-05-14T13:36:44.390 に答える
2

前提条件が満たされない場合に備えて、アサーションまたは例外をスローするだけです。

Warrior::Warrior(uint health, uint maxHealth) :
    m_health(health),
    m_maxHealth(maxHealth)
{
    assert(health <= maxHealth); 
    // or throw exception
    throw std::logic_error("explanation");
}

つまり、呼び出し元がどの値を取るかを決定する合理的な理由は何ですか? 呼び出し元が maxHealth よりも大きなヘルス値を設定した場合は、クラス ロジックに違反しているため、すぐに失敗し、これについて呼び出し元に通知することを提案します。本来はソフトウェアの問題なので、隠すのは好ましくないと思います。

例外またはアサーションは、そのような問題を早期に見つけて認識するのに役立つ明示的なメカニズムです。

于 2013-05-14T13:38:50.960 に答える
2
template <typename T>
class RangeBoundValue
{
public:
    RangeBoundValue(const T _min, const T _max) : _min(_min), _max(_max) {}

    void setValue(T val)
    {
        if (val < _min) _value = _min;
        else if (val > _max) _value = _max;
        else _value = val;
    }

    T value() const {return _value;}

private:
    const T _min;
    const T _max;
    T       _value;
};

内部でこのヘルパー クラスを使用するWarrior

class Warrior
{
...
private:
    RangeBoundValue<uint> _health;
};
于 2013-05-14T13:37:45.830 に答える
1

コンストラクターへの引数リストに health>max_health が指定されている場合に、例外をスローするように人々が提案していることに、私は非常に驚いています。たとえば、すべてのクリーチャーが最大ヘルスまでの一定量のヘルスで復活し、場合によってはこれが最大ヘルスを超える復活の実装があるとします。カップリングを避けるために、このメソッドがすべてのオブジェクトを保存する必要はありません。これらの引数をコンストラクターに送信する前に、それをテストするメソッドを追加できますが、これも複雑になります。最大ヘルスは通常、ヘルスの上限として実装されます。これは単純な制約であり、他のものから切り離されているため、独自の制約を適用するのは戦士の責任です。

于 2013-05-14T14:13:08.437 に答える