第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 課題の要件
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 課題の要件
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 課題の要件
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;
}
---
まとめ
本章で実装した内容: