第3章:テストとデバッグ
はじめに
本章では、CPP Module 07のテストとデバッグ方法を解説します。
---
1. swap/min/maxのテスト
1.1 基本型のテスト
void testBasicTypes() {
std::cout << "=== Basic Types ===" << std::endl;
// int
int a = 1, b = 2;
std::cout << "Before swap: a=" << a << ", b=" << b << std::endl;
::swap(a, b);
std::cout << "After swap: a=" << a << ", b=" << b << std::endl;
std::cout << "min: " << ::min(a, b) << std::endl;
std::cout << "max: " << ::max(a, b) << std::endl;
// double
double x = 1.5, y = 2.5;
::swap(x, y);
std::cout << "min(double): " << ::min(x, y) << std::endl;
// char
char c1 = 'a', c2 = 'z';
std::cout << "min(char): " << ::min(c1, c2) << std::endl;
}
1.2 同値テスト
void testEquality() {
std::cout << "=== Equality Test ===" << std::endl;
int a = 42, b = 42;
std::cout << "a=" << a << ", b=" << b << std::endl;
// 同じ値の場合は2番目を返す
std::cout << "min address: " << &::min(a, b) << std::endl;
std::cout << "b address: " << &b << std::endl;
// min(a, b)はbのアドレスを返すべき
std::cout << "max address: " << &::max(a, b) << std::endl;
std::cout << "b address: " << &b << std::endl;
// max(a, b)もbのアドレスを返すべき
}
1.3 カスタム型のテスト
class Complex {
public:
double real, imag;
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
bool operator<(const Complex& other) const {
return (real * real + imag * imag) <
(other.real * other.real + other.imag * other.imag);
}
bool operator>(const Complex& other) const {
return other < *this;
}
};
void testCustomType() {
std::cout << "=== Custom Type ===" << std::endl;
Complex c1(3, 4); // magnitude = 5
Complex c2(1, 1); // magnitude ≈ 1.41
Complex minC = ::min(c1, c2);
std::cout << "min: (" << minC.real << ", " << minC.imag << ")" << std::endl;
}
---
2. iterのテスト
2.1 様々な関数のテスト
template <typename T>
void print(const T& value) {
std::cout << value << " ";
}
template <typename T>
void square(T& value) {
value = value * value;
}
void testIter() {
std::cout << "=== Iter Test ===" << std::endl;
int arr[] = {1, 2, 3, 4, 5};
size_t len = 5;
std::cout << "Original: ";
iter(arr, len, print<int>);
std::cout << std::endl;
iter(arr, len, square<int>);
std::cout << "Squared: ";
iter(arr, len, print<int>);
std::cout << std::endl;
}
2.2 空配列のテスト
void testEmptyArray() {
std::cout << "=== Empty Array ===" << std::endl;
int* empty = NULL;
iter(empty, 0, print<int>); // 何も起きない
std::cout << "Empty array handled" << std::endl;
}
2.3 const配列のテスト
void testConstArray() {
std::cout << "=== Const Array ===" << std::endl;
const int arr[] = {1, 2, 3};
iter(arr, 3, print<int>);
std::cout << std::endl;
}
---
3. Arrayのテスト
3.1 基本機能テスト
void testArrayBasic() {
std::cout << "=== Array Basic ===" << std::endl;
// デフォルトコンストラクタ
Array<int> empty;
assert(empty.size() == 0);
std::cout << "Empty array: OK" << std::endl;
// サイズ付きコンストラクタ
Array<int> arr(10);
assert(arr.size() == 10);
std::cout << "Sized array: OK" << std::endl;
// 要素アクセス
arr[0] = 42;
assert(arr[0] == 42);
std::cout << "Element access: OK" << std::endl;
}
3.2 深いコピーテスト
void testDeepCopy() {
std::cout << "=== Deep Copy ===" << std::endl;
Array<int> original(5);
for (unsigned int i = 0; i < original.size(); i++) {
original[i] = i * 10;
}
// コピーコンストラクタ
Array<int> copy1(original);
for (unsigned int i = 0; i < copy1.size(); i++) {
assert(copy1[i] == original[i]);
}
std::cout << "Copy constructor: OK" << std::endl;
// 代入演算子
Array<int> copy2;
copy2 = original;
for (unsigned int i = 0; i < copy2.size(); i++) {
assert(copy2[i] == original[i]);
}
std::cout << "Assignment operator: OK" << std::endl;
// 独立性確認
original[0] = 999;
assert(copy1[0] != original[0]);
assert(copy2[0] != original[0]);
std::cout << "Independence: OK" << std::endl;
}
3.3 例外テスト
void testExceptions() {
std::cout << "=== Exception Test ===" << std::endl;
Array<int> arr(5);
// 範囲内アクセス
try {
arr[4] = 42;
std::cout << "Valid access: OK" << std::endl;
} catch (...) {
std::cout << "Valid access: FAIL" << std::endl;
}
// 範囲外アクセス
try {
arr[5] = 42;
std::cout << "Out of bounds: FAIL (no exception)" << std::endl;
} catch (const std::exception& e) {
std::cout << "Out of bounds: OK (" << e.what() << ")" << std::endl;
}
// 空配列へのアクセス
Array<int> empty;
try {
empty[0] = 42;
std::cout << "Empty array access: FAIL" << std::endl;
} catch (const std::exception& e) {
std::cout << "Empty array access: OK" << std::endl;
}
}
3.4 複雑な型のテスト
void testComplexTypes() {
std::cout << "=== Complex Types ===" << std::endl;
// std::string
Array<std::string> strArr(3);
strArr[0] = "Hello";
strArr[1] = "World";
strArr[2] = "!";
for (unsigned int i = 0; i < strArr.size(); i++) {
std::cout << strArr[i] << " ";
}
std::cout << std::endl;
// ネストされたArray
Array< Array<int> > nested(2);
nested[0] = Array<int>(3);
nested[1] = Array<int>(3);
nested[0][0] = 1;
nested[1][0] = 2;
std::cout << "Nested array: OK" << std::endl;
}
---
4. よくある間違い
4.1 参照 vs 値渡し
// 間違い: 値渡し
template <typename T>
void swap(T a, T b) { // コピーが渡される
T temp = a;
a = b;
b = temp;
}
// 正しい: 参照渡し
template <typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
4.2 const参照の戻り値
// 間違い: 一時オブジェクトを返す
template <typename T>
T min(T a, T b) {
return (a < b) ? a : b;
}
// 正しい: const参照で返す
template <typename T>
T const& min(T const& a, T const& b) {
return (a < b) ? a : b;
}
4.3 配列のデフォルト初期化
// 間違い: 不定値
Array(unsigned int n) : _elements(new T[n]), _size(n) {}
// 正しい: 値初期化
Array(unsigned int n) : _elements(new T[n]()), _size(n) {}
4.4 代入演算子での自己代入
// 間違い: 自己代入でクラッシュ
Array& operator=(const Array& other) {
delete[] _elements; // 自己代入時に自分を削除
_elements = new T[other._size];
// ...
}
// 正しい: 自己代入チェック
Array& operator=(const Array& other) {
if (this != &other) {
delete[] _elements;
// ...
}
return *this;
}
4.5 operator[]のconst版
// 間違い: const版がない
T& operator[](unsigned int index);
const Array<int>& ref = arr;
ref[0]; // エラー!const版がない
// 正しい: 両方定義
T& operator[](unsigned int index);
const T& operator[](unsigned int index) const;
---
5. 提出前チェックリスト
ex00
- [ ] swapが値を交換する
- [ ] minが小さい方を返す
- [ ] maxが大きい方を返す
- [ ] 同値の場合は2番目を返す
- [ ] 様々な型で動作する
- [ ] 配列の各要素に関数を適用
- [ ] const配列でも動作
- [ ] 空配列で問題なし
- [ ] テンプレート関数を渡せる
- [ ] デフォルトコンストラクタで空配列
- [ ] サイズ付きコンストラクタでデフォルト初期化
- [ ] 深いコピーが正しく動作
- [ ] operator[]で要素アクセス
- [ ] const版のoperator[]がある
- [ ] 範囲外で例外をthrow
- [ ] メモリリークなし
- 関数テンプレート: 様々な型での動作
- イテレーション: 配列と関数ポインタの連携
- クラステンプレート: コピー、アクセス、例外処理
ex01
ex02
---
まとめ
CPP Module 07のテストでは以下を確認: