第2章:課題の実装
はじめに
本章では、CPP Module 07の各演習を実装します。
---
1. ex00: Start with a few functions
1.1 課題の要件
- swap: 2つの値を交換
- min: 2つの値の小さい方を返す
- max: 2つの値の大きい方を返す
- 同じ値の場合は2番目を返す
1.2 実装
// whatever.hpp
#ifndef WHATEVER_HPP
#define WHATEVER_HPP
template <typename T>
void swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
template <typename T>
T const& min(T const& a, T const& b) {
return (a < b) ? a : b;
}
template <typename T>
T const& max(T const& a, T const& b) {
return (a > b) ? a : b;
}
#endif
// main.cpp
#include "whatever.hpp"
#include <iostream>
#include <string>
int main() {
int a = 2;
int b = 3;
::swap(a, b);
std::cout << "a = " << a << ", b = " << b << std::endl;
std::cout << "min(a, b) = " << ::min(a, b) << std::endl;
std::cout << "max(a, b) = " << ::max(a, b) << std::endl;
std::string c = "chaine1";
std::string d = "chaine2";
::swap(c, d);
std::cout << "c = " << c << ", d = " << d << std::endl;
std::cout << "min(c, d) = " << ::min(c, d) << std::endl;
std::cout << "max(c, d) = " << ::max(c, d) << std::endl;
return 0;
}
1.3 期待される出力
a = 3, b = 2
min(a, b) = 2
max(a, b) = 3
c = chaine2, d = chaine1
min(c, d) = chaine1
max(c, d) = chaine2
1.4 なぜconst参照を返すか
// 参照を返すことで、一時オブジェクトのコピーを避ける
template <typename T>
T const& min(T const& a, T const& b) {
return (a < b) ? a : b;
}
// 値を返す場合、コピーが発生
template <typename T>
T min(T a, T b) {
return (a < b) ? a : b; // コピーが発生
}
---
2. ex01: Iter
2.1 課題の要件
2.2 実装
// iter.hpp
#ifndef ITER_HPP
#define ITER_HPP
#include <cstddef>
template <typename T>
void iter(T* array, size_t length, void (*func)(T&)) {
for (size_t i = 0; i < length; i++) {
func(array[i]);
}
}
// const版
template <typename T>
void iter(const T* array, size_t length, void (*func)(const T&)) {
for (size_t i = 0; i < length; i++) {
func(array[i]);
}
}
#endif
// main.cpp
#include "iter.hpp"
#include <iostream>
#include <string>
template <typename T>
void print(const T& value) {
std::cout << value << std::endl;
}
template <typename T>
void increment(T& value) {
value++;
}
void toUpper(std::string& str) {
for (size_t i = 0; i < str.length(); i++) {
str[i] = std::toupper(str[i]);
}
}
int main() {
std::cout << "=== Integer Array ===" << std::endl;
int intArr[] = {1, 2, 3, 4, 5};
size_t intLen = sizeof(intArr) / sizeof(intArr[0]);
std::cout << "Original:" << std::endl;
iter(intArr, intLen, print);
iter(intArr, intLen, increment);
std::cout << "After increment:" << std::endl;
iter(intArr, intLen, print);
std::cout << "\n=== String Array ===" << std::endl;
std::string strArr[] = {"hello", "world", "cpp"};
size_t strLen = sizeof(strArr) / sizeof(strArr[0]);
std::cout << "Original:" << std::endl;
iter(strArr, strLen, print);
iter(strArr, strLen, toUpper);
std::cout << "After toUpper:" << std::endl;
iter(strArr, strLen, print);
return 0;
}
2.3 期待される出力
=== Integer Array ===
Original:
1
2
3
4
5
After increment:
2
3
4
5
6
=== String Array ===
Original:
hello
world
cpp
After toUpper:
HELLO
WORLD
CPP
2.4 テンプレート関数を渡す
// テンプレート関数を渡す場合、明示的なインスタンス化が必要
template <typename T>
void genericPrint(const T& value) {
std::cout << value << std::endl;
}
// 使用時に明示的に型を指定
iter(intArr, intLen, genericPrint<int>);
iter(strArr, strLen, genericPrint<std::string>);
---
3. ex02: Array
3.1 課題の要件
3.2 実装
// Array.hpp
#ifndef ARRAY_HPP
#define ARRAY_HPP
#include <exception>
#include <cstddef>
template <typename T>
class Array {
private:
T* _elements;
unsigned int _size;
public:
// コンストラクタ
Array() : _elements(NULL), _size(0) {}
Array(unsigned int n) : _elements(new T[n]()), _size(n) {}
Array(const Array& other) : _elements(NULL), _size(0) {
*this = other;
}
// 代入演算子
Array& operator=(const Array& other) {
if (this != &other) {
delete[] _elements;
_size = other._size;
_elements = new T[_size];
for (unsigned int i = 0; i < _size; i++) {
_elements[i] = other._elements[i];
}
}
return *this;
}
// デストラクタ
~Array() {
delete[] _elements;
}
// 添字演算子
T& operator[](unsigned int index) {
if (index >= _size) {
throw OutOfBoundsException();
}
return _elements[index];
}
const T& operator[](unsigned int index) const {
if (index >= _size) {
throw OutOfBoundsException();
}
return _elements[index];
}
// サイズ取得
unsigned int size() const {
return _size;
}
// 例外クラス
class OutOfBoundsException : public std::exception {
public:
const char* what() const throw() {
return "Index out of bounds";
}
};
};
#endif
// main.cpp
#include "Array.hpp"
#include <iostream>
#include <string>
int main() {
std::cout << "=== Default Constructor ===" << std::endl;
Array<int> empty;
std::cout << "Empty array size: " << empty.size() << std::endl;
std::cout << "\n=== Sized Constructor ===" << std::endl;
Array<int> arr(5);
std::cout << "Array size: " << arr.size() << std::endl;
std::cout << "Default values: ";
for (unsigned int i = 0; i < arr.size(); i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
std::cout << "\n=== Assignment ===" << std::endl;
for (unsigned int i = 0; i < arr.size(); i++) {
arr[i] = i * 10;
}
std::cout << "After assignment: ";
for (unsigned int i = 0; i < arr.size(); i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
std::cout << "\n=== Copy Constructor ===" << std::endl;
Array<int> copy(arr);
std::cout << "Copy: ";
for (unsigned int i = 0; i < copy.size(); i++) {
std::cout << copy[i] << " ";
}
std::cout << std::endl;
std::cout << "\n=== Deep Copy Test ===" << std::endl;
arr[0] = 999;
std::cout << "Original[0]: " << arr[0] << std::endl;
std::cout << "Copy[0]: " << copy[0] << std::endl;
std::cout << "\n=== Out of Bounds ===" << std::endl;
try {
arr[100] = 42;
} catch (const std::exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
}
std::cout << "\n=== String Array ===" << std::endl;
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;
return 0;
}
3.3 期待される出力
=== Default Constructor ===
Empty array size: 0
=== Sized Constructor ===
Array size: 5
Default values: 0 0 0 0 0
=== Assignment ===
After assignment: 0 10 20 30 40
=== Copy Constructor ===
Copy: 0 10 20 30 40
=== Deep Copy Test ===
Original[0]: 999
Copy[0]: 0
=== Out of Bounds ===
Exception: Index out of bounds
=== String Array ===
Hello World !
3.4 デフォルト初期化
// new T[n]() で値初期化(ゼロ初期化)
Array(unsigned int n) : _elements(new T[n]()), _size(n) {}
// new T[n] だとデフォルト初期化(不定値の可能性)
// POD型では初期化されない
---
4. 実装の注意点
4.1 std::swapとの衝突
// 課題のswapとstd::swapの衝突を避ける
#include "whatever.hpp"
::swap(a, b); // グローバルスコープのswap
std::swap(a, b); // 標準ライブラリのswap
4.2 const正当性
// const版と非const版の両方が必要
T& operator[](unsigned int index);
const T& operator[](unsigned int index) const;
// 使用
Array<int> arr(5);
const Array<int>& constRef = arr;
arr[0] = 10; // 非const版
int x = constRef[0]; // const版
4.3 例外安全性
// 代入演算子での例外安全性
Array& operator=(const Array& other) {
if (this != &other) {
// 新しい配列を先に作成
T* newElements = new T[other._size];
for (unsigned int i = 0; i < other._size; i++) {
newElements[i] = other._elements[i];
}
// 成功したら古い配列を削除
delete[] _elements;
_elements = newElements;
_size = other._size;
}
return *this;
}
---
まとめ
本章で実装した内容: