第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番目を返す
  • [ ] 様々な型で動作する
  • ex01

  • [ ] 配列の各要素に関数を適用
  • [ ] const配列でも動作
  • [ ] 空配列で問題なし
  • [ ] テンプレート関数を渡せる
  • ex02

  • [ ] デフォルトコンストラクタで空配列
  • [ ] サイズ付きコンストラクタでデフォルト初期化
  • [ ] 深いコピーが正しく動作
  • [ ] operator[]で要素アクセス
  • [ ] const版のoperator[]がある
  • [ ] 範囲外で例外をthrow
  • [ ] メモリリークなし
  • ---

    まとめ

    CPP Module 07のテストでは以下を確認:

  • 関数テンプレート: 様々な型での動作
  • イテレーション: 配列と関数ポインタの連携
  • クラステンプレート: コピー、アクセス、例外処理