第2章:課題の実装

はじめに

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

---

1. ex00: BraiiiiiiinnnzzzZ

1.1 課題の要件

  • Zombieクラスを作成
  • スタックとヒープでのオブジェクト生成を比較
  • newZombie関数でヒープに生成
  • randomChump関数でスタックに生成
  • 1.2 実装

    // Zombie.hpp
    #ifndef ZOMBIE_HPP
    #define ZOMBIE_HPP
    
    #include <string>
    
    class Zombie {
    private:
        std::string _name;
    
    public:
        Zombie();
        Zombie(const std::string& name);
        ~Zombie();
        void announce() const;
    };
    
    Zombie* newZombie(std::string name);
    void randomChump(std::string name);
    
    #endif
    
    // Zombie.cpp
    #include "Zombie.hpp"
    #include <iostream>
    
    Zombie::Zombie() : _name("Unnamed") {}
    
    Zombie::Zombie(const std::string& name) : _name(name) {}
    
    Zombie::~Zombie() {
        std::cout << _name << " is destroyed" << std::endl;
    }
    
    void Zombie::announce() const {
        std::cout << _name << ": BraiiiiiiinnnzzzZ..." << std::endl;
    }
    
    // newZombie.cpp
    #include "Zombie.hpp"
    
    Zombie* newZombie(std::string name) {
        return new Zombie(name);
    }
    
    // randomChump.cpp
    #include "Zombie.hpp"
    
    void randomChump(std::string name) {
        Zombie zombie(name);
        zombie.announce();
    }
    
    // main.cpp
    #include "Zombie.hpp"
    
    int main() {
        // ヒープに生成
        Zombie* heapZombie = newZombie("HeapZombie");
        heapZombie->announce();
    
        // スタックに生成
        randomChump("StackZombie");
    
        // ヒープのゾンビを削除
        delete heapZombie;
    
        return 0;
    }
    

    ---

    2. ex01: Moar brainz!

    2.1 課題の要件

  • 複数のZombieを一度に生成
  • zombieHorde関数で配列を返す
  • 2.2 実装

    // Zombie.hpp
    #ifndef ZOMBIE_HPP
    #define ZOMBIE_HPP
    
    #include <string>
    
    class Zombie {
    private:
        std::string _name;
    
    public:
        Zombie();
        ~Zombie();
        void setName(const std::string& name);
        void announce() const;
    };
    
    Zombie* zombieHorde(int N, std::string name);
    
    #endif
    
    // zombieHorde.cpp
    #include "Zombie.hpp"
    
    Zombie* zombieHorde(int N, std::string name) {
        if (N <= 0)
            return NULL;
    
        Zombie* horde = new Zombie[N];
        for (int i = 0; i < N; i++) {
            horde[i].setName(name);
        }
        return horde;
    }
    
    // main.cpp
    #include "Zombie.hpp"
    
    int main() {
        int N = 5;
        Zombie* horde = zombieHorde(N, "Zombie");
    
        for (int i = 0; i < N; i++) {
            horde[i].announce();
        }
    
        delete[] horde;
        return 0;
    }
    

    ---

    3. ex02: HI THIS IS BRAIN

    3.1 課題の要件

  • 文字列変数、ポインタ、参照のアドレスと値を表示
  • 3.2 実装

    #include <iostream>
    #include <string>
    
    int main() {
        std::string str = "HI THIS IS BRAIN";
        std::string* stringPTR = &str;
        std::string& stringREF = str;
    
        std::cout << "Address of str:       " << &str << std::endl;
        std::cout << "Address of stringPTR: " << stringPTR << std::endl;
        std::cout << "Address of stringREF: " << &stringREF << std::endl;
    
        std::cout << "Value of str:         " << str << std::endl;
        std::cout << "Value of stringPTR:   " << *stringPTR << std::endl;
        std::cout << "Value of stringREF:   " << stringREF << std::endl;
    
        return 0;
    }
    

    ---

    4. ex03: Unnecessary violence

    4.1 課題の要件

  • Weaponクラスを作成
  • HumanAとHumanBクラスを作成
  • HumanAは参照で武器を持つ
  • HumanBはポインタで武器を持つ
  • 4.2 実装

    // Weapon.hpp
    #ifndef WEAPON_HPP
    #define WEAPON_HPP
    
    #include <string>
    
    class Weapon {
    private:
        std::string _type;
    
    public:
        Weapon(const std::string& type);
        const std::string& getType() const;
        void setType(const std::string& type);
    };
    
    #endif
    
    // HumanA.hpp
    #ifndef HUMANA_HPP
    #define HUMANA_HPP
    
    #include "Weapon.hpp"
    #include <string>
    
    class HumanA {
    private:
        std::string _name;
        Weapon& _weapon;  // 参照:常に武器を持つ
    
    public:
        HumanA(const std::string& name, Weapon& weapon);
        void attack() const;
    };
    
    #endif
    
    // HumanB.hpp
    #ifndef HUMANB_HPP
    #define HUMANB_HPP
    
    #include "Weapon.hpp"
    #include <string>
    
    class HumanB {
    private:
        std::string _name;
        Weapon* _weapon;  // ポインタ:武器を持たない可能性
    
    public:
        HumanB(const std::string& name);
        void setWeapon(Weapon& weapon);
        void attack() const;
    };
    
    #endif
    
    // HumanA.cpp
    #include "HumanA.hpp"
    #include <iostream>
    
    HumanA::HumanA(const std::string& name, Weapon& weapon)
        : _name(name), _weapon(weapon) {}
    
    void HumanA::attack() const {
        std::cout << _name << " attacks with their " << _weapon.getType() << std::endl;
    }
    
    // HumanB.cpp
    #include "HumanB.hpp"
    #include <iostream>
    
    HumanB::HumanB(const std::string& name) : _name(name), _weapon(NULL) {}
    
    void HumanB::setWeapon(Weapon& weapon) {
        _weapon = &weapon;
    }
    
    void HumanB::attack() const {
        if (_weapon) {
            std::cout << _name << " attacks with their " << _weapon->getType() << std::endl;
        } else {
            std::cout << _name << " has no weapon" << std::endl;
        }
    }
    

    ---

    5. ex04: Sed is for losers

    5.1 課題の要件

  • ファイル内の文字列を置換
  • std::string::replaceは使用禁止
  • 5.2 実装

    #include <iostream>
    #include <fstream>
    #include <string>
    
    std::string replaceAll(const std::string& content,
                           const std::string& s1,
                           const std::string& s2) {
        std::string result;
        size_t pos = 0;
        size_t prevPos = 0;
    
        while ((pos = content.find(s1, prevPos)) != std::string::npos) {
            result += content.substr(prevPos, pos - prevPos);
            result += s2;
            prevPos = pos + s1.length();
        }
        result += content.substr(prevPos);
    
        return result;
    }
    
    int main(int argc, char** argv) {
        if (argc != 4) {
            std::cerr << "Usage: " << argv[0] << " <filename> <s1> <s2>" << std::endl;
            return 1;
        }
    
        std::string filename = argv[1];
        std::string s1 = argv[2];
        std::string s2 = argv[3];
    
        if (s1.empty()) {
            std::cerr << "Error: s1 cannot be empty" << std::endl;
            return 1;
        }
    
        std::ifstream inFile(filename.c_str());
        if (!inFile) {
            std::cerr << "Error: Cannot open file" << std::endl;
            return 1;
        }
    
        std::string content;
        std::string line;
        while (std::getline(inFile, line)) {
            if (!content.empty())
                content += '\n';
            content += line;
        }
        inFile.close();
    
        std::string replaced = replaceAll(content, s1, s2);
    
        std::ofstream outFile((filename + ".replace").c_str());
        if (!outFile) {
            std::cerr << "Error: Cannot create output file" << std::endl;
            return 1;
        }
        outFile << replaced;
        outFile.close();
    
        return 0;
    }
    

    ---

    6. ex05: Harl 2.0

    6.1 課題の要件

  • Harlクラスにポインタtoメンバ関数を使用
  • 4つのログレベル: DEBUG, INFO, WARNING, ERROR
  • 6.2 実装

    // Harl.hpp
    #ifndef HARL_HPP
    #define HARL_HPP
    
    #include <string>
    
    class Harl {
    private:
        void debug();
        void info();
        void warning();
        void error();
    
    public:
        void complain(std::string level);
    };
    
    #endif
    
    // Harl.cpp
    #include "Harl.hpp"
    #include <iostream>
    
    void Harl::debug() {
        std::cout << "[ DEBUG ]" << std::endl;
        std::cout << "I love having extra bacon..." << std::endl;
    }
    
    void Harl::info() {
        std::cout << "[ INFO ]" << std::endl;
        std::cout << "I cannot believe adding extra bacon costs more money..." << std::endl;
    }
    
    void Harl::warning() {
        std::cout << "[ WARNING ]" << std::endl;
        std::cout << "I think I deserve to have some extra bacon for free..." << std::endl;
    }
    
    void Harl::error() {
        std::cout << "[ ERROR ]" << std::endl;
        std::cout << "This is unacceptable! I want to speak to the manager now." << std::endl;
    }
    
    void Harl::complain(std::string level) {
        void (Harl::*functions[4])() = {
            &Harl::debug,
            &Harl::info,
            &Harl::warning,
            &Harl::error
        };
        std::string levels[4] = {"DEBUG", "INFO", "WARNING", "ERROR"};
    
        for (int i = 0; i < 4; i++) {
            if (levels[i] == level) {
                (this->*functions[i])();
                return;
            }
        }
        std::cout << "Unknown level: " << level << std::endl;
    }
    

    ---

    7. ex06: Harl filter

    7.1 課題の要件

  • switch文を使用
  • 指定レベル以上のログを表示(fall-through)
  • 7.2 実装

    // harlFilter.cpp
    #include "Harl.hpp"
    #include <iostream>
    
    int main(int argc, char** argv) {
        if (argc != 2) {
            std::cerr << "Usage: " << argv[0] << " <level>" << std::endl;
            return 1;
        }
    
        Harl harl;
        std::string level = argv[1];
        int levelIndex = -1;
    
        if (level == "DEBUG") levelIndex = 0;
        else if (level == "INFO") levelIndex = 1;
        else if (level == "WARNING") levelIndex = 2;
        else if (level == "ERROR") levelIndex = 3;
    
        switch (levelIndex) {
            case 0:
                harl.debug();
                // fall through
            case 1:
                harl.info();
                // fall through
            case 2:
                harl.warning();
                // fall through
            case 3:
                harl.error();
                break;
            default:
                std::cout << "[ Probably complaining about insignificant problems ]" << std::endl;
        }
    
        return 0;
    }
    

    ---

    まとめ

    本章で実装した内容:

  • ex00-01: 動的メモリ割り当て
  • ex02: ポインタと参照の違い
  • ex03: 参照とポインタの使い分け
  • ex04: ファイル入出力
  • ex05-06: ポインタtoメンバ関数とswitch文