第3章:テストとデバッグ
はじめに
本章では、CPP Module 04のテストとデバッグ方法を解説します。
---
1. 仮想関数のテスト
1.1 ポリモーフィズムの確認
void testPolymorphism() {
std::cout << "=== Polymorphism Test ===" << std::endl;
const Animal* animal = new Animal();
const Animal* dog = new Dog();
const Animal* cat = new Cat();
std::cout << "Animal type: " << animal->getType() << std::endl;
std::cout << "Dog type: " << dog->getType() << std::endl;
std::cout << "Cat type: " << cat->getType() << std::endl;
std::cout << "\nMaking sounds:" << std::endl;
animal->makeSound(); // "..."
dog->makeSound(); // "Woof!"
cat->makeSound(); // "Meow!"
delete animal;
delete dog;
delete cat;
}
1.2 WrongAnimalとの比較
void testWrongPolymorphism() {
std::cout << "=== Wrong Polymorphism Test ===" << std::endl;
const WrongAnimal* wrongAnimal = new WrongAnimal();
const WrongAnimal* wrongCat = new WrongCat();
std::cout << "WrongCat type: " << wrongCat->getType() << std::endl;
std::cout << "\nMaking sounds:" << std::endl;
wrongAnimal->makeSound(); // WrongAnimalの音
wrongCat->makeSound(); // WrongAnimalの音!(virtualなし)
delete wrongAnimal;
delete wrongCat;
}
---
2. 仮想デストラクタのテスト
2.1 正しいデストラクタ順序
void testVirtualDestructor() {
std::cout << "=== Virtual Destructor Test ===" << std::endl;
Animal* dog = new Dog();
std::cout << "\nDeleting through base pointer:" << std::endl;
delete dog;
// 期待: Dog destructor → Animal destructor
}
2.2 Valgrindでメモリリーク確認
valgrind --leak-check=full ./animals
# 期待される結果(ex01以降):
# All heap blocks were freed -- no leaks are possible
---
3. 深いコピーのテスト
3.1 コピーの独立性確認
void testDeepCopy() {
std::cout << "=== Deep Copy Test ===" << std::endl;
Dog original;
original.getBrain()->ideas[0] = "Original idea";
original.getBrain()->ideas[1] = "Another idea";
std::cout << "\n--- Copy Constructor ---" << std::endl;
Dog copy1(original);
std::cout << "\n--- Assignment Operator ---" << std::endl;
Dog copy2;
copy2 = original;
// アドレスが異なることを確認
std::cout << "\nBrain addresses:" << std::endl;
std::cout << "Original: " << original.getBrain() << std::endl;
std::cout << "Copy1: " << copy1.getBrain() << std::endl;
std::cout << "Copy2: " << copy2.getBrain() << std::endl;
// 内容の確認
std::cout << "\nIdeas:" << std::endl;
std::cout << "Original[0]: " << original.getBrain()->ideas[0] << std::endl;
std::cout << "Copy1[0]: " << copy1.getBrain()->ideas[0] << std::endl;
std::cout << "Copy2[0]: " << copy2.getBrain()->ideas[0] << std::endl;
// 独立性の確認
copy1.getBrain()->ideas[0] = "Modified in copy1";
std::cout << "\nAfter modifying copy1:" << std::endl;
std::cout << "Original[0]: " << original.getBrain()->ideas[0] << std::endl;
std::cout << "Copy1[0]: " << copy1.getBrain()->ideas[0] << std::endl;
}
3.2 配列のテスト
void testAnimalArray() {
std::cout << "=== Animal Array Test ===" << std::endl;
const int count = 10;
Animal* animals[count];
std::cout << "\n--- Creating animals ---" << std::endl;
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--- Making sounds ---" << std::endl;
for (int i = 0; i < count; i++) {
std::cout << animals[i]->getType() << ": ";
animals[i]->makeSound();
}
std::cout << "\n--- Deleting animals ---" << std::endl;
for (int i = 0; i < count; i++) {
delete animals[i];
}
}
---
4. 抽象クラスのテスト
4.1 コンパイル時チェック
void testAbstractClass() {
// 以下はコンパイルエラーになるべき
// Animal animal; // Error: cannot instantiate abstract class
// ポインタ/参照は可能
Animal* ptr = new Dog();
Animal& ref = *ptr;
ptr->makeSound();
ref.makeSound();
delete ptr;
}
---
5. インターフェースのテスト(ex03)
5.1 基本機能テスト
void testMateriaBasic() {
std::cout << "=== Materia Basic Test ===" << std::endl;
IMateriaSource* src = new MateriaSource();
src->learnMateria(new Ice());
src->learnMateria(new Cure());
ICharacter* me = new Character("Hero");
AMateria* ice = src->createMateria("ice");
AMateria* cure = src->createMateria("cure");
me->equip(ice);
me->equip(cure);
ICharacter* target = new Character("Enemy");
me->use(0, *target); // ice
me->use(1, *target); // cure
delete target;
delete me;
delete src;
}
5.2 unequipテスト
void testUnequip() {
std::cout << "=== Unequip Test ===" << std::endl;
Character hero("Hero");
AMateria* ice = new Ice();
hero.equip(ice);
hero.unequip(0); // iceはdeleteされない
// メモリリーク防止のため手動で削除
delete ice;
}
5.3 深いコピーテスト
void testCharacterDeepCopy() {
std::cout << "=== Character Deep Copy Test ===" << std::endl;
Character original("Original");
original.equip(new Ice());
original.equip(new Cure());
Character copy = original;
ICharacter* target = new Character("Target");
std::cout << "Original using:" << std::endl;
original.use(0, *target);
original.use(1, *target);
std::cout << "Copy using:" << std::endl;
copy.use(0, *target);
copy.use(1, *target);
delete target;
}
---
6. よくある間違い
6.1 仮想デストラクタの欠如
// 間違い
class Animal {
public:
~Animal() { } // 非仮想デストラクタ
};
class Dog : public Animal {
Brain* brain;
public:
Dog() : brain(new Brain()) { }
~Dog() { delete brain; } // 呼ばれない可能性
};
// 正しい
class Animal {
public:
virtual ~Animal() { } // 仮想デストラクタ
};
6.2 浅いコピー
// 間違い
Dog::Dog(const Dog& other) : Animal(other) {
brain = other.brain; // 浅いコピー
}
// 正しい
Dog::Dog(const Dog& other) : Animal(other) {
brain = new Brain(*other.brain); // 深いコピー
}
6.3 代入演算子でのリーク
// 間違い
Dog& Dog::operator=(const Dog& other) {
brain = new Brain(*other.brain); // 古いbrainがリーク
return *this;
}
// 正しい
Dog& Dog::operator=(const Dog& other) {
if (this != &other) {
delete brain; // 古いbrainを解放
brain = new Brain(*other.brain);
}
return *this;
}
6.4 unequipでのdelete
// 間違い
void Character::unequip(int idx) {
delete inventory[idx]; // 外部でまだ使う可能性
inventory[idx] = NULL;
}
// 正しい
void Character::unequip(int idx) {
inventory[idx] = NULL; // deleteしない
}
6.5 clone()の実装忘れ
// 間違い
AMateria* Ice::clone() const {
return new Ice(); // 現在の状態がコピーされない
}
// 正しい
AMateria* Ice::clone() const {
return new Ice(*this); // コピーコンストラクタを使用
}
---
7. 提出前チェックリスト
ex00
- [ ] Animalクラスが正しく動作
- [ ] Dog/Catが正しくオーバーライド
- [ ] 仮想デストラクタがある
- [ ] WrongAnimal/WrongCatで違いを確認
- [ ] Brainクラスが正しく動作
- [ ] Dog/CatにBrain*がある
- [ ] 深いコピーが正しく動作
- [ ] Valgrindでメモリリークなし
- [ ] Animalが抽象クラス
- [ ] makeSound()が純粋仮想関数
- [ ] Animalを直接インスタンス化できない
- [ ] AMateria抽象クラスが正しい
- [ ] Ice/Cureがclone()を実装
- [ ] Characterが4つのスロットを持つ
- [ ] unequipがdeleteしない
- [ ] MateriaSourceがファクトリとして動作
- [ ] 深いコピーが正しい
- [ ] メモリリークなし
- 仮想関数: 動的バインディングが正しく動作
- 仮想デストラクタ: メモリリークなし
- 深いコピー: 独立したオブジェクト
- 抽象クラス: インスタンス化不可
- インターフェース: 正しい実装とメモリ管理
ex01
ex02
ex03
---
まとめ
CPP Module 04のテストでは以下を確認: