第3章:テストとデバッグ

はじめに

本章では、CPP Module 03のテストとデバッグ方法を解説します。

---

1. コンストラクタ/デストラクタの順序確認

1.1 単一継承のテスト

void testSingleInheritance() {
    std::cout << "=== Single Inheritance Test ===" << std::endl;
    std::cout << "Creating ScavTrap:" << std::endl;
    ScavTrap scav("Test");
    std::cout << "End of scope" << std::endl;
}

期待される出力:

=== Single Inheritance Test ===
Creating ScavTrap:
ClapTrap constructor called for Test
ScavTrap constructor called for Test
End of scope
ScavTrap destructor called for Test
ClapTrap destructor called for Test

チェックポイント:

  • コンストラクタ: 基底 → 派生
  • デストラクタ: 派生 → 基底

1.2 多重継承のテスト

void testDiamondInheritance() {
    std::cout << "=== Diamond Inheritance Test ===" << std::endl;
    std::cout << "Creating DiamondTrap:" << std::endl;
    DiamondTrap diamond("Test");
    std::cout << "End of scope" << std::endl;
}

期待される出力:

=== Diamond Inheritance Test ===
Creating DiamondTrap:
ClapTrap constructor called for Test_clap_name
ScavTrap constructor called for Test
FragTrap constructor called for Test
DiamondTrap constructor called for Test
End of scope
DiamondTrap destructor called for Test
FragTrap destructor called for Test
ScavTrap destructor called for Test
ClapTrap destructor called for Test_clap_name

チェックポイント:

  • ClapTrapは1回だけ呼ばれる(仮想継承)
  • コンストラクタ: ClapTrap → ScavTrap → FragTrap → DiamondTrap
  • デストラクタ: 逆順

---

2. 属性値のテスト

2.1 初期値の確認

void testInitialValues() {
    std::cout << "=== Initial Values Test ===" << std::endl;

    ClapTrap clap("Clap");
    ScavTrap scav("Scav");
    FragTrap frag("Frag");
    DiamondTrap diamond("Diamond");

    std::cout << "\nClapTrap values:" << std::endl;
    std::cout << "HP: " << clap.getHitPoints() << " (expected: 10)" << std::endl;
    std::cout << "EP: " << clap.getEnergyPoints() << " (expected: 10)" << std::endl;
    std::cout << "AD: " << clap.getAttackDamage() << " (expected: 0)" << std::endl;

    std::cout << "\nScavTrap values:" << std::endl;
    std::cout << "HP: " << scav.getHitPoints() << " (expected: 100)" << std::endl;
    std::cout << "EP: " << scav.getEnergyPoints() << " (expected: 50)" << std::endl;
    std::cout << "AD: " << scav.getAttackDamage() << " (expected: 20)" << std::endl;

    std::cout << "\nFragTrap values:" << std::endl;
    std::cout << "HP: " << frag.getHitPoints() << " (expected: 100)" << std::endl;
    std::cout << "EP: " << frag.getEnergyPoints() << " (expected: 100)" << std::endl;
    std::cout << "AD: " << frag.getAttackDamage() << " (expected: 30)" << std::endl;

    std::cout << "\nDiamondTrap values:" << std::endl;
    std::cout << "HP: " << diamond.getHitPoints() << " (expected: 100)" << std::endl;
    std::cout << "EP: " << diamond.getEnergyPoints() << " (expected: 50)" << std::endl;
    std::cout << "AD: " << diamond.getAttackDamage() << " (expected: 30)" << std::endl;
}

2.2 属性サマリー

| クラス | HP | EP | AD | |--------|---:|---:|---:| | ClapTrap | 10 | 10 | 0 | | ScavTrap | 100 | 50 | 20 | | FragTrap | 100 | 100 | 30 | | DiamondTrap | 100 | 50 | 30 |

---

3. アクションのテスト

3.1 attack/takeDamage/beRepaired

void testActions() {
    std::cout << "=== Actions Test ===" << std::endl;

    ClapTrap clap("Clap");

    // 攻撃テスト
    clap.attack("Target");
    std::cout << "EP after attack: " << clap.getEnergyPoints() << std::endl;

    // ダメージテスト
    clap.takeDamage(5);
    std::cout << "HP after damage: " << clap.getHitPoints() << std::endl;

    // 回復テスト
    clap.beRepaired(3);
    std::cout << "HP after repair: " << clap.getHitPoints() << std::endl;
    std::cout << "EP after repair: " << clap.getEnergyPoints() << std::endl;
}

3.2 エネルギー切れのテスト

void testEnergyDepletion() {
    std::cout << "=== Energy Depletion Test ===" << std::endl;

    ClapTrap clap("Clap");

    // 10回攻撃(エネルギーを使い切る)
    for (int i = 0; i < 10; i++) {
        clap.attack("Target");
    }

    std::cout << "EP after 10 attacks: " << clap.getEnergyPoints() << std::endl;

    // 11回目の攻撃(エネルギー切れ)
    clap.attack("Target");  // エラーメッセージが表示されるはず
}

3.3 HP切れのテスト

void testDeath() {
    std::cout << "=== Death Test ===" << std::endl;

    ClapTrap clap("Clap");

    // 致死ダメージ
    clap.takeDamage(15);
    std::cout << "HP after fatal damage: " << clap.getHitPoints() << std::endl;

    // 死亡後のアクション
    clap.attack("Target");   // 死亡メッセージ
    clap.beRepaired(10);     // 死亡メッセージ
}

---

4. メソッド解決のテスト

4.1 オーバーライドのテスト

void testMethodResolution() {
    std::cout << "=== Method Resolution Test ===" << std::endl;

    ClapTrap* clap = new ClapTrap("Clap");
    ClapTrap* scav = new ScavTrap("Scav");  // 基底クラスポインタ

    clap->attack("Target");  // ClapTrapのattack
    scav->attack("Target");  // ClapTrapのattack(virtualなし)

    delete clap;
    delete scav;
}

注意: CPP Module 03ではvirtualを使用しないため、基底クラスポインタでは基底クラスのメソッドが呼ばれます。

4.2 DiamondTrapのメソッド確認

void testDiamondMethods() {
    std::cout << "=== DiamondTrap Methods Test ===" << std::endl;

    DiamondTrap diamond("Diamond");

    diamond.attack("Target");       // ScavTrapのattack(using宣言)
    diamond.guardGate();            // ScavTrapから継承
    diamond.highFivesGuys();        // FragTrapから継承
    diamond.whoAmI();               // DiamondTrap独自
}

---

5. コピーのテスト

5.1 コピーコンストラクタ

void testCopyConstructor() {
    std::cout << "=== Copy Constructor Test ===" << std::endl;

    ScavTrap original("Original");
    original.takeDamage(20);

    ScavTrap copy(original);

    std::cout << "Original HP: " << original.getHitPoints() << std::endl;
    std::cout << "Copy HP: " << copy.getHitPoints() << std::endl;
}

5.2 コピー代入演算子

void testAssignmentOperator() {
    std::cout << "=== Assignment Operator Test ===" << std::endl;

    ScavTrap a("A");
    ScavTrap b("B");

    a.takeDamage(30);

    b = a;

    std::cout << "A HP: " << a.getHitPoints() << std::endl;
    std::cout << "B HP: " << b.getHitPoints() << std::endl;
    std::cout << "B Name: " << b.getName() << std::endl;
}

---

6. よくある間違い

6.1 属性をprivateにしてしまう

// 間違い
class ClapTrap {
private:
    std::string _name;
    int _hitPoints;  // 派生クラスからアクセス不可
};

class ScavTrap : public ClapTrap {
    ScavTrap() {
        _hitPoints = 100;  // エラー!
    }
};

// 正しい
class ClapTrap {
protected:  // protectedにする
    std::string _name;
    int _hitPoints;
};

6.2 仮想継承を忘れる

// 間違い(仮想継承なし)
class ScavTrap : public ClapTrap { };
class FragTrap : public ClapTrap { };
class DiamondTrap : public ScavTrap, public FragTrap {
    // ClapTrapが2つ存在
};

// 正しい
class ScavTrap : virtual public ClapTrap { };
class FragTrap : virtual public ClapTrap { };
class DiamondTrap : public ScavTrap, public FragTrap {
    // ClapTrapは1つ
};

6.3 DiamondTrapでClapTrapを初期化しない

// 間違い
DiamondTrap::DiamondTrap(const std::string& name)
    : ScavTrap(name),  // ClapTrapはデフォルトコンストラクタで初期化
      FragTrap(name) { }

// 正しい
DiamondTrap::DiamondTrap(const std::string& name)
    : ClapTrap(name + "_clap_name"),  // 仮想基底クラスを明示的に初期化
      ScavTrap(name),
      FragTrap(name) { }

6.4 デストラクタの順序を誤解

デストラクタは自動的に呼ばれます。派生クラスのデストラクタで基底クラスのデストラクタを明示的に呼ぶ必要はありません。

// 間違い
ScavTrap::~ScavTrap() {
    ClapTrap::~ClapTrap();  // 不要!二重に呼ばれる
}

// 正しい
ScavTrap::~ScavTrap() {
    // ScavTrap固有のクリーンアップのみ
}

---

7. 提出前チェックリスト

ex00

  • [ ] ClapTrapが正しく動作する
  • [ ] attack/takeDamage/beRepairedが動作
  • [ ] エネルギー/HP切れの処理
  • ex01

  • [ ] ScavTrapがClapTrapを継承
  • [ ] 初期値が正しい(HP:100, EP:50, AD:20)
  • [ ] guardGate()が動作
  • [ ] コンストラクタ/デストラクタの順序
  • ex02

  • [ ] FragTrapがClapTrapを継承
  • [ ] 初期値が正しい(HP:100, EP:100, AD:30)
  • [ ] highFivesGuys()が動作
  • ex03

  • [ ] 仮想継承を使用
  • [ ] DiamondTrapの初期値が正しい
  • [ ] whoAmI()が正しく動作
  • [ ] 2つの異なる_nameを持つ
  • [ ] コンストラクタ/デストラクタの順序
  • ---

    まとめ

    CPP Module 03のテストでは以下を確認:

  • コンストラクタ/デストラクタ順序: 基底→派生、派生→基底
  • 属性値: 各クラスで正しい初期値
  • メソッド解決: オーバーライドとusing宣言
  • 仮想継承: ダイヤモンド問題の解決