第2章:課題の実装

はじめに

本章では、CPP Module 03の各演習を実装します。

---

1. ex00: Aaaaand... OPEN!

1.1 課題の要件

  • ClapTrapクラスを作成
  • 属性: name, hitPoints(10), energyPoints(10), attackDamage(0)
  • attack, takeDamage, beRepairedメソッド
  • 1.2 実装

    // ClapTrap.hpp
    #ifndef CLAPTRAP_HPP
    #define CLAPTRAP_HPP
    
    #include <string>
    #include <iostream>
    
    class ClapTrap {
    protected:
        std::string _name;
        int         _hitPoints;
        int         _energyPoints;
        int         _attackDamage;
    
    public:
        ClapTrap();
        ClapTrap(const std::string& name);
        ClapTrap(const ClapTrap& other);
        ClapTrap& operator=(const ClapTrap& other);
        ~ClapTrap();
    
        void attack(const std::string& target);
        void takeDamage(unsigned int amount);
        void beRepaired(unsigned int amount);
    
        // ゲッター(デバッグ用)
        std::string getName() const;
        int getHitPoints() const;
        int getEnergyPoints() const;
        int getAttackDamage() const;
    };
    
    #endif
    
    // ClapTrap.cpp
    #include "ClapTrap.hpp"
    
    ClapTrap::ClapTrap()
        : _name("Unknown"), _hitPoints(10), _energyPoints(10), _attackDamage(0) {
        std::cout << "ClapTrap default constructor called" << std::endl;
    }
    
    ClapTrap::ClapTrap(const std::string& name)
        : _name(name), _hitPoints(10), _energyPoints(10), _attackDamage(0) {
        std::cout << "ClapTrap constructor called for " << name << std::endl;
    }
    
    ClapTrap::ClapTrap(const ClapTrap& other)
        : _name(other._name),
          _hitPoints(other._hitPoints),
          _energyPoints(other._energyPoints),
          _attackDamage(other._attackDamage) {
        std::cout << "ClapTrap copy constructor called" << std::endl;
    }
    
    ClapTrap& ClapTrap::operator=(const ClapTrap& other) {
        std::cout << "ClapTrap copy assignment operator called" << std::endl;
        if (this != &other) {
            _name = other._name;
            _hitPoints = other._hitPoints;
            _energyPoints = other._energyPoints;
            _attackDamage = other._attackDamage;
        }
        return *this;
    }
    
    ClapTrap::~ClapTrap() {
        std::cout << "ClapTrap destructor called for " << _name << std::endl;
    }
    
    void ClapTrap::attack(const std::string& target) {
        if (_hitPoints <= 0) {
            std::cout << "ClapTrap " << _name << " is dead and cannot attack!" << std::endl;
            return;
        }
        if (_energyPoints <= 0) {
            std::cout << "ClapTrap " << _name << " has no energy to attack!" << std::endl;
            return;
        }
        _energyPoints--;
        std::cout << "ClapTrap " << _name << " attacks " << target
                  << ", causing " << _attackDamage << " points of damage!" << std::endl;
    }
    
    void ClapTrap::takeDamage(unsigned int amount) {
        _hitPoints -= amount;
        if (_hitPoints < 0)
            _hitPoints = 0;
        std::cout << "ClapTrap " << _name << " takes " << amount
                  << " points of damage! HP: " << _hitPoints << std::endl;
    }
    
    void ClapTrap::beRepaired(unsigned int amount) {
        if (_hitPoints <= 0) {
            std::cout << "ClapTrap " << _name << " is dead and cannot repair!" << std::endl;
            return;
        }
        if (_energyPoints <= 0) {
            std::cout << "ClapTrap " << _name << " has no energy to repair!" << std::endl;
            return;
        }
        _energyPoints--;
        _hitPoints += amount;
        std::cout << "ClapTrap " << _name << " repairs itself for " << amount
                  << " HP! HP: " << _hitPoints << std::endl;
    }
    
    std::string ClapTrap::getName() const { return _name; }
    int ClapTrap::getHitPoints() const { return _hitPoints; }
    int ClapTrap::getEnergyPoints() const { return _energyPoints; }
    int ClapTrap::getAttackDamage() const { return _attackDamage; }
    
    // main.cpp
    #include "ClapTrap.hpp"
    
    int main() {
        ClapTrap a("Robot");
        ClapTrap b("Droid");
    
        a.attack("Droid");
        b.takeDamage(0);
        b.beRepaired(5);
    
        // エネルギー消費テスト
        for (int i = 0; i < 10; i++) {
            a.attack("target");
        }
        a.attack("target");  // エネルギー切れ
    
        return 0;
    }
    

    ---

    2. ex01: Serena, my love!

    2.1 課題の要件

  • ScavTrapクラス(ClapTrapを継承)
  • hitPoints: 100, energyPoints: 50, attackDamage: 20
  • guardGate()メソッドを追加
  • コンストラクタ/デストラクタでメッセージを表示
  • 2.2 実装

    // ScavTrap.hpp
    #ifndef SCAVTRAP_HPP
    #define SCAVTRAP_HPP
    
    #include "ClapTrap.hpp"
    
    class ScavTrap : public ClapTrap {
    public:
        ScavTrap();
        ScavTrap(const std::string& name);
        ScavTrap(const ScavTrap& other);
        ScavTrap& operator=(const ScavTrap& other);
        ~ScavTrap();
    
        void attack(const std::string& target);
        void guardGate();
    };
    
    #endif
    
    // ScavTrap.cpp
    #include "ScavTrap.hpp"
    
    ScavTrap::ScavTrap() : ClapTrap() {
        _hitPoints = 100;
        _energyPoints = 50;
        _attackDamage = 20;
        std::cout << "ScavTrap default constructor called" << std::endl;
    }
    
    ScavTrap::ScavTrap(const std::string& name) : ClapTrap(name) {
        _hitPoints = 100;
        _energyPoints = 50;
        _attackDamage = 20;
        std::cout << "ScavTrap constructor called for " << name << std::endl;
    }
    
    ScavTrap::ScavTrap(const ScavTrap& other) : ClapTrap(other) {
        std::cout << "ScavTrap copy constructor called" << std::endl;
    }
    
    ScavTrap& ScavTrap::operator=(const ScavTrap& other) {
        std::cout << "ScavTrap copy assignment operator called" << std::endl;
        if (this != &other) {
            ClapTrap::operator=(other);
        }
        return *this;
    }
    
    ScavTrap::~ScavTrap() {
        std::cout << "ScavTrap destructor called for " << _name << std::endl;
    }
    
    void ScavTrap::attack(const std::string& target) {
        if (_hitPoints <= 0) {
            std::cout << "ScavTrap " << _name << " is dead and cannot attack!" << std::endl;
            return;
        }
        if (_energyPoints <= 0) {
            std::cout << "ScavTrap " << _name << " has no energy to attack!" << std::endl;
            return;
        }
        _energyPoints--;
        std::cout << "ScavTrap " << _name << " attacks " << target
                  << ", causing " << _attackDamage << " points of damage!" << std::endl;
    }
    
    void ScavTrap::guardGate() {
        std::cout << "ScavTrap " << _name << " is now in Gate keeper mode!" << std::endl;
    }
    
    // main.cpp
    #include "ClapTrap.hpp"
    #include "ScavTrap.hpp"
    
    int main() {
        std::cout << "=== Creating ClapTrap ===" << std::endl;
        ClapTrap clap("Clappy");
    
        std::cout << "\n=== Creating ScavTrap ===" << std::endl;
        ScavTrap scav("Scavvy");
    
        std::cout << "\n=== Actions ===" << std::endl;
        clap.attack("Enemy");
        scav.attack("Enemy");
        scav.guardGate();
    
        std::cout << "\n=== Destruction ===" << std::endl;
        return 0;
    }
    

    2.3 期待される出力

    === Creating ClapTrap ===
    ClapTrap constructor called for Clappy
    
    === Creating ScavTrap ===
    ClapTrap constructor called for Scavvy
    ScavTrap constructor called for Scavvy
    
    === Actions ===
    ClapTrap Clappy attacks Enemy, causing 0 points of damage!
    ScavTrap Scavvy attacks Enemy, causing 20 points of damage!
    ScavTrap Scavvy is now in Gate keeper mode!
    
    === Destruction ===
    ScavTrap destructor called for Scavvy
    ClapTrap destructor called for Scavvy
    ClapTrap destructor called for Clappy
    

    ---

    3. ex02: Repetitive work

    3.1 課題の要件

  • FragTrapクラス(ClapTrapを継承)
  • hitPoints: 100, energyPoints: 100, attackDamage: 30
  • highFivesGuys()メソッドを追加
  • 3.2 実装

    // FragTrap.hpp
    #ifndef FRAGTRAP_HPP
    #define FRAGTRAP_HPP
    
    #include "ClapTrap.hpp"
    
    class FragTrap : public ClapTrap {
    public:
        FragTrap();
        FragTrap(const std::string& name);
        FragTrap(const FragTrap& other);
        FragTrap& operator=(const FragTrap& other);
        ~FragTrap();
    
        void highFivesGuys();
    };
    
    #endif
    
    // FragTrap.cpp
    #include "FragTrap.hpp"
    
    FragTrap::FragTrap() : ClapTrap() {
        _hitPoints = 100;
        _energyPoints = 100;
        _attackDamage = 30;
        std::cout << "FragTrap default constructor called" << std::endl;
    }
    
    FragTrap::FragTrap(const std::string& name) : ClapTrap(name) {
        _hitPoints = 100;
        _energyPoints = 100;
        _attackDamage = 30;
        std::cout << "FragTrap constructor called for " << name << std::endl;
    }
    
    FragTrap::FragTrap(const FragTrap& other) : ClapTrap(other) {
        std::cout << "FragTrap copy constructor called" << std::endl;
    }
    
    FragTrap& FragTrap::operator=(const FragTrap& other) {
        std::cout << "FragTrap copy assignment operator called" << std::endl;
        if (this != &other) {
            ClapTrap::operator=(other);
        }
        return *this;
    }
    
    FragTrap::~FragTrap() {
        std::cout << "FragTrap destructor called for " << _name << std::endl;
    }
    
    void FragTrap::highFivesGuys() {
        std::cout << "FragTrap " << _name << " requests a high five!" << std::endl;
    }
    
    // main.cpp
    #include "ClapTrap.hpp"
    #include "ScavTrap.hpp"
    #include "FragTrap.hpp"
    
    int main() {
        std::cout << "=== Creating Traps ===" << std::endl;
        ClapTrap clap("Clappy");
        ScavTrap scav("Scavvy");
        FragTrap frag("Fraggy");
    
        std::cout << "\n=== Actions ===" << std::endl;
        clap.attack("Enemy");
        scav.attack("Enemy");
        scav.guardGate();
        frag.attack("Enemy");
        frag.highFivesGuys();
    
        std::cout << "\n=== Destruction ===" << std::endl;
        return 0;
    }
    

    ---

    4. ex03: Now it's weird!(Bonus)

    4.1 課題の要件

  • DiamondTrap(ScavTrapとFragTrapを継承)
  • ClapTrapはvirtual継承
  • 独自の_name属性(ClapTrap::_nameとは別)
  • hitPoints: FragTrap, energyPoints: ScavTrap, attackDamage: FragTrap
  • whoAmI()メソッド

4.2 仮想継承の修正

まず、ScavTrapとFragTrapを仮想継承に変更:

// ScavTrap.hpp
class ScavTrap : virtual public ClapTrap {
    // ...
};

// FragTrap.hpp
class FragTrap : virtual public ClapTrap {
    // ...
};

4.3 DiamondTrapの実装

// DiamondTrap.hpp
#ifndef DIAMONDTRAP_HPP
#define DIAMONDTRAP_HPP

#include "ScavTrap.hpp"
#include "FragTrap.hpp"

class DiamondTrap : public ScavTrap, public FragTrap {
private:
    std::string _name;

public:
    DiamondTrap();
    DiamondTrap(const std::string& name);
    DiamondTrap(const DiamondTrap& other);
    DiamondTrap& operator=(const DiamondTrap& other);
    ~DiamondTrap();

    void whoAmI();
    using ScavTrap::attack;  // ScavTrapのattackを使用
};

#endif

// DiamondTrap.cpp
#include "DiamondTrap.hpp"

DiamondTrap::DiamondTrap()
    : ClapTrap("Unknown_clap_name"),
      ScavTrap("Unknown"),
      FragTrap("Unknown"),
      _name("Unknown") {
    _hitPoints = FragTrap::_hitPoints;        // 100
    _energyPoints = ScavTrap::_energyPoints;  // 50
    _attackDamage = FragTrap::_attackDamage;  // 30
    std::cout << "DiamondTrap default constructor called" << std::endl;
}

DiamondTrap::DiamondTrap(const std::string& name)
    : ClapTrap(name + "_clap_name"),
      ScavTrap(name),
      FragTrap(name),
      _name(name) {
    _hitPoints = 100;      // FragTrapから
    _energyPoints = 50;    // ScavTrapから
    _attackDamage = 30;    // FragTrapから
    std::cout << "DiamondTrap constructor called for " << name << std::endl;
}

DiamondTrap::DiamondTrap(const DiamondTrap& other)
    : ClapTrap(other),
      ScavTrap(other),
      FragTrap(other),
      _name(other._name) {
    std::cout << "DiamondTrap copy constructor called" << std::endl;
}

DiamondTrap& DiamondTrap::operator=(const DiamondTrap& other) {
    std::cout << "DiamondTrap copy assignment operator called" << std::endl;
    if (this != &other) {
        ClapTrap::operator=(other);
        _name = other._name;
    }
    return *this;
}

DiamondTrap::~DiamondTrap() {
    std::cout << "DiamondTrap destructor called for " << _name << std::endl;
}

void DiamondTrap::whoAmI() {
    std::cout << "I am DiamondTrap " << _name
              << ", also known as ClapTrap " << ClapTrap::_name << std::endl;
}

// main.cpp
#include "DiamondTrap.hpp"

int main() {
    std::cout << "=== Creating DiamondTrap ===" << std::endl;
    DiamondTrap diamond("Diamond");

    std::cout << "\n=== DiamondTrap Info ===" << std::endl;
    std::cout << "HP: " << diamond.getHitPoints() << std::endl;       // 100
    std::cout << "EP: " << diamond.getEnergyPoints() << std::endl;    // 50
    std::cout << "AD: " << diamond.getAttackDamage() << std::endl;    // 30

    std::cout << "\n=== Actions ===" << std::endl;
    diamond.attack("Enemy");       // ScavTrapのattack
    diamond.guardGate();           // ScavTrapのメソッド
    diamond.highFivesGuys();       // FragTrapのメソッド
    diamond.whoAmI();              // DiamondTrap独自

    std::cout << "\n=== Destruction ===" << std::endl;
    return 0;
}

4.4 コンストラクタ呼び出し順序

DiamondTrap生成時の順序:

  • ClapTrap(仮想基底クラス、最初)
  • ScavTrap
  • FragTrap
  • DiamondTrap

デストラクタは逆順:

  • DiamondTrap
  • FragTrap
  • ScavTrap
  • ClapTrap
  • 4.5 期待される出力

    === Creating DiamondTrap ===
    ClapTrap constructor called for Diamond_clap_name
    ScavTrap constructor called for Diamond
    FragTrap constructor called for Diamond
    DiamondTrap constructor called for Diamond
    
    === DiamondTrap Info ===
    HP: 100
    EP: 50
    AD: 30
    
    === Actions ===
    ScavTrap Diamond attacks Enemy, causing 30 points of damage!
    ScavTrap Diamond is now in Gate keeper mode!
    FragTrap Diamond requests a high five!
    I am DiamondTrap Diamond, also known as ClapTrap Diamond_clap_name
    
    === Destruction ===
    DiamondTrap destructor called for Diamond
    FragTrap destructor called for Diamond
    ScavTrap destructor called for Diamond
    ClapTrap destructor called for Diamond_clap_name
    

    ---

    まとめ

    本章で実装した内容:

  • ex00: ClapTrap基底クラス
  • ex01: ScavTrap派生クラス(単一継承)
  • ex02: FragTrap派生クラス(単一継承)
  • ex03: DiamondTrap(多重継承、仮想継承)