第2章:課題の実装

はじめに

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

---

1. ex00: Polymorphism

1.1 課題の要件

  • Animalクラス(基底クラス)
  • Dog/Catクラス(派生クラス)
  • 仮想関数makeSound()
  • WrongAnimal/WrongCatクラス(仮想関数なしの比較用)
  • 1.2 Animalクラス

    // Animal.hpp
    #ifndef ANIMAL_HPP
    #define ANIMAL_HPP
    
    #include <string>
    #include <iostream>
    
    class Animal {
    protected:
        std::string type;
    
    public:
        Animal();
        Animal(const Animal& other);
        Animal& operator=(const Animal& other);
        virtual ~Animal();
    
        virtual void makeSound() const;
        std::string getType() const;
    };
    
    #endif
    
    // Animal.cpp
    #include "Animal.hpp"
    
    Animal::Animal() : type("Animal") {
        std::cout << "Animal default constructor called" << std::endl;
    }
    
    Animal::Animal(const Animal& other) : type(other.type) {
        std::cout << "Animal copy constructor called" << std::endl;
    }
    
    Animal& Animal::operator=(const Animal& other) {
        std::cout << "Animal assignment operator called" << std::endl;
        if (this != &other) {
            type = other.type;
        }
        return *this;
    }
    
    Animal::~Animal() {
        std::cout << "Animal destructor called" << std::endl;
    }
    
    void Animal::makeSound() const {
        std::cout << "..." << std::endl;
    }
    
    std::string Animal::getType() const {
        return type;
    }
    

    1.3 Dogクラス

    // Dog.hpp
    #ifndef DOG_HPP
    #define DOG_HPP
    
    #include "Animal.hpp"
    
    class Dog : public Animal {
    public:
        Dog();
        Dog(const Dog& other);
        Dog& operator=(const Dog& other);
        ~Dog();
    
        void makeSound() const override;
    };
    
    #endif
    
    // Dog.cpp
    #include "Dog.hpp"
    
    Dog::Dog() : Animal() {
        type = "Dog";
        std::cout << "Dog default constructor called" << std::endl;
    }
    
    Dog::Dog(const Dog& other) : Animal(other) {
        std::cout << "Dog copy constructor called" << std::endl;
    }
    
    Dog& Dog::operator=(const Dog& other) {
        std::cout << "Dog assignment operator called" << std::endl;
        if (this != &other) {
            Animal::operator=(other);
        }
        return *this;
    }
    
    Dog::~Dog() {
        std::cout << "Dog destructor called" << std::endl;
    }
    
    void Dog::makeSound() const {
        std::cout << "Woof! Woof!" << std::endl;
    }
    

    1.4 Catクラス

    // Cat.hpp
    #ifndef CAT_HPP
    #define CAT_HPP
    
    #include "Animal.hpp"
    
    class Cat : public Animal {
    public:
        Cat();
        Cat(const Cat& other);
        Cat& operator=(const Cat& other);
        ~Cat();
    
        void makeSound() const override;
    };
    
    #endif
    
    // Cat.cpp
    #include "Cat.hpp"
    
    Cat::Cat() : Animal() {
        type = "Cat";
        std::cout << "Cat default constructor called" << std::endl;
    }
    
    Cat::Cat(const Cat& other) : Animal(other) {
        std::cout << "Cat copy constructor called" << std::endl;
    }
    
    Cat& Cat::operator=(const Cat& other) {
        std::cout << "Cat assignment operator called" << std::endl;
        if (this != &other) {
            Animal::operator=(other);
        }
        return *this;
    }
    
    Cat::~Cat() {
        std::cout << "Cat destructor called" << std::endl;
    }
    
    void Cat::makeSound() const {
        std::cout << "Meow! Meow!" << std::endl;
    }
    

    1.5 WrongAnimal/WrongCat(比較用)

    // WrongAnimal.hpp
    #ifndef WRONGANIMAL_HPP
    #define WRONGANIMAL_HPP
    
    #include <string>
    #include <iostream>
    
    class WrongAnimal {
    protected:
        std::string type;
    
    public:
        WrongAnimal();
        WrongAnimal(const WrongAnimal& other);
        WrongAnimal& operator=(const WrongAnimal& other);
        ~WrongAnimal();  // 非仮想!
    
        void makeSound() const;  // 非仮想!
        std::string getType() const;
    };
    
    #endif
    
    // WrongCat.hpp
    #ifndef WRONGCAT_HPP
    #define WRONGCAT_HPP
    
    #include "WrongAnimal.hpp"
    
    class WrongCat : public WrongAnimal {
    public:
        WrongCat();
        WrongCat(const WrongCat& other);
        WrongCat& operator=(const WrongCat& other);
        ~WrongCat();
    
        void makeSound() const;
    };
    
    #endif
    

    1.6 main.cpp

    #include "Animal.hpp"
    #include "Dog.hpp"
    #include "Cat.hpp"
    #include "WrongAnimal.hpp"
    #include "WrongCat.hpp"
    
    int main() {
        std::cout << "=== Correct polymorphism ===" << std::endl;
        const Animal* meta = new Animal();
        const Animal* j = new Dog();
        const Animal* i = new Cat();
    
        std::cout << j->getType() << " " << std::endl;
        std::cout << i->getType() << " " << std::endl;
    
        i->makeSound();  // "Meow!"
        j->makeSound();  // "Woof!"
        meta->makeSound();
    
        delete meta;
        delete j;
        delete i;
    
        std::cout << "\n=== Wrong polymorphism ===" << std::endl;
        const WrongAnimal* wrongMeta = new WrongAnimal();
        const WrongAnimal* wrongCat = new WrongCat();
    
        std::cout << wrongCat->getType() << " " << std::endl;
        wrongCat->makeSound();  // WrongAnimalのmakeSound!
    
        delete wrongMeta;
        delete wrongCat;
    
        return 0;
    }
    

    ---

    2. ex01: I don't want to set the world on fire

    2.1 課題の要件

  • Brainクラス(ideas配列を持つ)
  • Dog/Catがそれぞれprivate Brain*を持つ
  • 深いコピーの実装
  • メモリリークなし
  • 2.2 Brainクラス

    // Brain.hpp
    #ifndef BRAIN_HPP
    #define BRAIN_HPP
    
    #include <string>
    #include <iostream>
    
    class Brain {
    public:
        std::string ideas[100];
    
        Brain();
        Brain(const Brain& other);
        Brain& operator=(const Brain& other);
        ~Brain();
    };
    
    #endif
    
    // Brain.cpp
    #include "Brain.hpp"
    
    Brain::Brain() {
        std::cout << "Brain default constructor called" << std::endl;
        for (int i = 0; i < 100; i++) {
            ideas[i] = "";
        }
    }
    
    Brain::Brain(const Brain& other) {
        std::cout << "Brain copy constructor called" << std::endl;
        for (int i = 0; i < 100; i++) {
            ideas[i] = other.ideas[i];
        }
    }
    
    Brain& Brain::operator=(const Brain& other) {
        std::cout << "Brain assignment operator called" << std::endl;
        if (this != &other) {
            for (int i = 0; i < 100; i++) {
                ideas[i] = other.ideas[i];
            }
        }
        return *this;
    }
    
    Brain::~Brain() {
        std::cout << "Brain destructor called" << std::endl;
    }
    

    2.3 DogクラスにBrainを追加

    // Dog.hpp
    #ifndef DOG_HPP
    #define DOG_HPP
    
    #include "Animal.hpp"
    #include "Brain.hpp"
    
    class Dog : public Animal {
    private:
        Brain* brain;
    
    public:
        Dog();
        Dog(const Dog& other);
        Dog& operator=(const Dog& other);
        ~Dog();
    
        void makeSound() const override;
        Brain* getBrain() const;
    };
    
    #endif
    
    // Dog.cpp
    #include "Dog.hpp"
    
    Dog::Dog() : Animal(), brain(new Brain()) {
        type = "Dog";
        std::cout << "Dog default constructor called" << std::endl;
    }
    
    Dog::Dog(const Dog& other) : Animal(other), brain(new Brain(*other.brain)) {
        std::cout << "Dog copy constructor called" << std::endl;
    }
    
    Dog& Dog::operator=(const Dog& other) {
        std::cout << "Dog assignment operator called" << std::endl;
        if (this != &other) {
            Animal::operator=(other);
            delete brain;
            brain = new Brain(*other.brain);
        }
        return *this;
    }
    
    Dog::~Dog() {
        std::cout << "Dog destructor called" << std::endl;
        delete brain;
    }
    
    void Dog::makeSound() const {
        std::cout << "Woof! Woof!" << std::endl;
    }
    
    Brain* Dog::getBrain() const {
        return brain;
    }
    

    2.4 CatクラスにBrainを追加

    // Cat.hpp
    #ifndef CAT_HPP
    #define CAT_HPP
    
    #include "Animal.hpp"
    #include "Brain.hpp"
    
    class Cat : public Animal {
    private:
        Brain* brain;
    
    public:
        Cat();
        Cat(const Cat& other);
        Cat& operator=(const Cat& other);
        ~Cat();
    
        void makeSound() const override;
        Brain* getBrain() const;
    };
    
    #endif
    
    // Cat.cpp
    #include "Cat.hpp"
    
    Cat::Cat() : Animal(), brain(new Brain()) {
        type = "Cat";
        std::cout << "Cat default constructor called" << std::endl;
    }
    
    Cat::Cat(const Cat& other) : Animal(other), brain(new Brain(*other.brain)) {
        std::cout << "Cat copy constructor called" << std::endl;
    }
    
    Cat& Cat::operator=(const Cat& other) {
        std::cout << "Cat assignment operator called" << std::endl;
        if (this != &other) {
            Animal::operator=(other);
            delete brain;
            brain = new Brain(*other.brain);
        }
        return *this;
    }
    
    Cat::~Cat() {
        std::cout << "Cat destructor called" << std::endl;
        delete brain;
    }
    
    void Cat::makeSound() const {
        std::cout << "Meow! Meow!" << std::endl;
    }
    
    Brain* Cat::getBrain() const {
        return brain;
    }
    

    2.5 main.cpp(テスト)

    #include "Animal.hpp"
    #include "Dog.hpp"
    #include "Cat.hpp"
    
    int main() {
        std::cout << "=== Array of Animals ===" << std::endl;
        const int count = 4;
        Animal* animals[count];
    
        for (int i = 0; i < count / 2; i++) {
            animals[i] = new Dog();
        }
        for (int i = count / 2; i < count; i++) {
            animals[i] = new Cat();
        }
    
        std::cout << "\n=== Deleting Animals ===" << std::endl;
        for (int i = 0; i < count; i++) {
            delete animals[i];
        }
    
        std::cout << "\n=== Deep Copy Test ===" << std::endl;
        Dog original;
        original.getBrain()->ideas[0] = "Chase the ball";
    
        Dog copy = original;  // コピーコンストラクタ
    
        std::cout << "Original idea: " << original.getBrain()->ideas[0] << std::endl;
        std::cout << "Copy idea: " << copy.getBrain()->ideas[0] << std::endl;
    
        // 深いコピーの確認
        copy.getBrain()->ideas[0] = "Eat treats";
    
        std::cout << "After modification:" << std::endl;
        std::cout << "Original idea: " << original.getBrain()->ideas[0] << std::endl;
        std::cout << "Copy idea: " << copy.getBrain()->ideas[0] << std::endl;
    
        return 0;
    }
    

    ---

    3. ex02: Abstract class

    3.1 課題の要件

  • AnimalクラスをAbstractにする
  • 直接インスタンス化を禁止
  • 3.2 Animalを抽象クラスに

    // Animal.hpp
    #ifndef ANIMAL_HPP
    #define ANIMAL_HPP
    
    #include <string>
    #include <iostream>
    
    class Animal {
    protected:
        std::string type;
    
    public:
        Animal();
        Animal(const Animal& other);
        Animal& operator=(const Animal& other);
        virtual ~Animal();
    
        virtual void makeSound() const = 0;  // 純粋仮想関数
        std::string getType() const;
    };
    
    #endif
    
    // Animal.cpp
    #include "Animal.hpp"
    
    Animal::Animal() : type("Animal") {
        std::cout << "Animal default constructor called" << std::endl;
    }
    
    Animal::Animal(const Animal& other) : type(other.type) {
        std::cout << "Animal copy constructor called" << std::endl;
    }
    
    Animal& Animal::operator=(const Animal& other) {
        std::cout << "Animal assignment operator called" << std::endl;
        if (this != &other) {
            type = other.type;
        }
        return *this;
    }
    
    Animal::~Animal() {
        std::cout << "Animal destructor called" << std::endl;
    }
    
    // makeSound()の実装は不要(純粋仮想関数)
    
    std::string Animal::getType() const {
        return type;
    }
    

    3.3 main.cpp

    #include "Animal.hpp"
    #include "Dog.hpp"
    #include "Cat.hpp"
    
    int main() {
        // Animal animal;  // コンパイルエラー!抽象クラス
    
        Animal* dog = new Dog();
        Animal* cat = new Cat();
    
        dog->makeSound();
        cat->makeSound();
    
        delete dog;
        delete cat;
    
        return 0;
    }
    

    ---

    4. ex03: Interface & recap(Bonus)

    4.1 課題の要件

  • AMateria抽象クラス
  • Ice/Cureクラス(AMateriaを継承)
  • ICharacterインターフェース
  • Characterクラス(ICharacterを実装)
  • IMateriaSourceインターフェース
  • MateriaSourceクラス(IMateriaSourceを実装)
  • 4.2 AMateria

    // AMateria.hpp
    #ifndef AMATERIA_HPP
    #define AMATERIA_HPP
    
    #include <string>
    #include <iostream>
    
    class ICharacter;  // 前方宣言
    
    class AMateria {
    protected:
        std::string type;
    
    public:
        AMateria();
        AMateria(std::string const& type);
        AMateria(const AMateria& other);
        AMateria& operator=(const AMateria& other);
        virtual ~AMateria();
    
        std::string const& getType() const;
        virtual AMateria* clone() const = 0;
        virtual void use(ICharacter& target) = 0;
    };
    
    #endif
    
    // AMateria.cpp
    #include "AMateria.hpp"
    
    AMateria::AMateria() : type("unknown") {}
    
    AMateria::AMateria(std::string const& type) : type(type) {}
    
    AMateria::AMateria(const AMateria& other) : type(other.type) {}
    
    AMateria& AMateria::operator=(const AMateria& other) {
        if (this != &other) {
            type = other.type;
        }
        return *this;
    }
    
    AMateria::~AMateria() {}
    
    std::string const& AMateria::getType() const {
        return type;
    }
    

    4.3 Ice/Cure

    // Ice.hpp
    #ifndef ICE_HPP
    #define ICE_HPP
    
    #include "AMateria.hpp"
    
    class Ice : public AMateria {
    public:
        Ice();
        Ice(const Ice& other);
        Ice& operator=(const Ice& other);
        ~Ice();
    
        AMateria* clone() const override;
        void use(ICharacter& target) override;
    };
    
    #endif
    
    // Ice.cpp
    #include "Ice.hpp"
    #include "ICharacter.hpp"
    
    Ice::Ice() : AMateria("ice") {}
    
    Ice::Ice(const Ice& other) : AMateria(other) {}
    
    Ice& Ice::operator=(const Ice& other) {
        if (this != &other) {
            AMateria::operator=(other);
        }
        return *this;
    }
    
    Ice::~Ice() {}
    
    AMateria* Ice::clone() const {
        return new Ice(*this);
    }
    
    void Ice::use(ICharacter& target) {
        std::cout << "* shoots an ice bolt at " << target.getName() << " *" << std::endl;
    }
    
    // Cure.hpp
    #ifndef CURE_HPP
    #define CURE_HPP
    
    #include "AMateria.hpp"
    
    class Cure : public AMateria {
    public:
        Cure();
        Cure(const Cure& other);
        Cure& operator=(const Cure& other);
        ~Cure();
    
        AMateria* clone() const override;
        void use(ICharacter& target) override;
    };
    
    #endif
    
    // Cure.cpp
    #include "Cure.hpp"
    #include "ICharacter.hpp"
    
    Cure::Cure() : AMateria("cure") {}
    
    Cure::Cure(const Cure& other) : AMateria(other) {}
    
    Cure& Cure::operator=(const Cure& other) {
        if (this != &other) {
            AMateria::operator=(other);
        }
        return *this;
    }
    
    Cure::~Cure() {}
    
    AMateria* Cure::clone() const {
        return new Cure(*this);
    }
    
    void Cure::use(ICharacter& target) {
        std::cout << "* heals " << target.getName() << "'s wounds *" << std::endl;
    }
    

    4.4 ICharacter/Character

    // ICharacter.hpp
    #ifndef ICHARACTER_HPP
    #define ICHARACTER_HPP
    
    #include <string>
    
    class AMateria;
    
    class ICharacter {
    public:
        virtual ~ICharacter() {}
        virtual std::string const& getName() const = 0;
        virtual void equip(AMateria* m) = 0;
        virtual void unequip(int idx) = 0;
        virtual void use(int idx, ICharacter& target) = 0;
    };
    
    #endif
    
    // Character.hpp
    #ifndef CHARACTER_HPP
    #define CHARACTER_HPP
    
    #include "ICharacter.hpp"
    #include "AMateria.hpp"
    
    class Character : public ICharacter {
    private:
        std::string name;
        AMateria* inventory[4];
    
    public:
        Character();
        Character(std::string const& name);
        Character(const Character& other);
        Character& operator=(const Character& other);
        ~Character();
    
        std::string const& getName() const override;
        void equip(AMateria* m) override;
        void unequip(int idx) override;
        void use(int idx, ICharacter& target) override;
    };
    
    #endif
    
    // Character.cpp
    #include "Character.hpp"
    
    Character::Character() : name("Unknown") {
        for (int i = 0; i < 4; i++) {
            inventory[i] = NULL;
        }
    }
    
    Character::Character(std::string const& name) : name(name) {
        for (int i = 0; i < 4; i++) {
            inventory[i] = NULL;
        }
    }
    
    Character::Character(const Character& other) : name(other.name) {
        for (int i = 0; i < 4; i++) {
            if (other.inventory[i]) {
                inventory[i] = other.inventory[i]->clone();
            } else {
                inventory[i] = NULL;
            }
        }
    }
    
    Character& Character::operator=(const Character& other) {
        if (this != &other) {
            name = other.name;
            for (int i = 0; i < 4; i++) {
                delete inventory[i];
                if (other.inventory[i]) {
                    inventory[i] = other.inventory[i]->clone();
                } else {
                    inventory[i] = NULL;
                }
            }
        }
        return *this;
    }
    
    Character::~Character() {
        for (int i = 0; i < 4; i++) {
            delete inventory[i];
        }
    }
    
    std::string const& Character::getName() const {
        return name;
    }
    
    void Character::equip(AMateria* m) {
        if (!m) return;
        for (int i = 0; i < 4; i++) {
            if (!inventory[i]) {
                inventory[i] = m;
                return;
            }
        }
    }
    
    void Character::unequip(int idx) {
        if (idx >= 0 && idx < 4) {
            inventory[idx] = NULL;  // deleteしない!
        }
    }
    
    void Character::use(int idx, ICharacter& target) {
        if (idx >= 0 && idx < 4 && inventory[idx]) {
            inventory[idx]->use(target);
        }
    }
    

    4.5 IMateriaSource/MateriaSource

    // IMateriaSource.hpp
    #ifndef IMATERIASOURCE_HPP
    #define IMATERIASOURCE_HPP
    
    #include "AMateria.hpp"
    
    class IMateriaSource {
    public:
        virtual ~IMateriaSource() {}
        virtual void learnMateria(AMateria*) = 0;
        virtual AMateria* createMateria(std::string const& type) = 0;
    };
    
    #endif
    
    // MateriaSource.hpp
    #ifndef MATERIASOURCE_HPP
    #define MATERIASOURCE_HPP
    
    #include "IMateriaSource.hpp"
    
    class MateriaSource : public IMateriaSource {
    private:
        AMateria* templates[4];
    
    public:
        MateriaSource();
        MateriaSource(const MateriaSource& other);
        MateriaSource& operator=(const MateriaSource& other);
        ~MateriaSource();
    
        void learnMateria(AMateria* m) override;
        AMateria* createMateria(std::string const& type) override;
    };
    
    #endif
    
    // MateriaSource.cpp
    #include "MateriaSource.hpp"
    
    MateriaSource::MateriaSource() {
        for (int i = 0; i < 4; i++) {
            templates[i] = NULL;
        }
    }
    
    MateriaSource::MateriaSource(const MateriaSource& other) {
        for (int i = 0; i < 4; i++) {
            if (other.templates[i]) {
                templates[i] = other.templates[i]->clone();
            } else {
                templates[i] = NULL;
            }
        }
    }
    
    MateriaSource& MateriaSource::operator=(const MateriaSource& other) {
        if (this != &other) {
            for (int i = 0; i < 4; i++) {
                delete templates[i];
                if (other.templates[i]) {
                    templates[i] = other.templates[i]->clone();
                } else {
                    templates[i] = NULL;
                }
            }
        }
        return *this;
    }
    
    MateriaSource::~MateriaSource() {
        for (int i = 0; i < 4; i++) {
            delete templates[i];
        }
    }
    
    void MateriaSource::learnMateria(AMateria* m) {
        if (!m) return;
        for (int i = 0; i < 4; i++) {
            if (!templates[i]) {
                templates[i] = m;
                return;
            }
        }
    }
    
    AMateria* MateriaSource::createMateria(std::string const& type) {
        for (int i = 0; i < 4; i++) {
            if (templates[i] && templates[i]->getType() == type) {
                return templates[i]->clone();
            }
        }
        return NULL;
    }
    

    4.6 main.cpp

    #include "MateriaSource.hpp"
    #include "Character.hpp"
    #include "Ice.hpp"
    #include "Cure.hpp"
    
    int main() {
        IMateriaSource* src = new MateriaSource();
        src->learnMateria(new Ice());
        src->learnMateria(new Cure());
    
        ICharacter* me = new Character("me");
    
        AMateria* tmp;
        tmp = src->createMateria("ice");
        me->equip(tmp);
        tmp = src->createMateria("cure");
        me->equip(tmp);
    
        ICharacter* bob = new Character("bob");
    
        me->use(0, *bob);  // * shoots an ice bolt at bob *
        me->use(1, *bob);  // * heals bob's wounds *
    
        delete bob;
        delete me;
        delete src;
    
        return 0;
    }
    

    ---

    まとめ

    本章で実装した内容:

  • ex00: 仮想関数によるポリモーフィズム
  • ex01: 深いコピーとメモリ管理
  • ex02: 抽象クラスと純粋仮想関数
  • ex03: インターフェースとファクトリパターン