はじめに
情報処理安全確保支援士の学習にあたってC++の知識が必要なので、自己学習しました。
その際に学習した内容をまとめたので、情報処理安全確保支援士の学習をする方やC++を短時間で学習したい方の参考になれば良いと思い教材を作成しました。
専門書を買うほどでもなくサクッと言語を学習したい方におすすめです。
目次
1.概要
2.基本的な構文
2-1.コメント
2-2.データ型と変数
2-3.演算子
2-4.制御文
2-5.関数
2-6.標準ライブラリ関数
3.オブジェクト指向プログラミング
3-1.クラスとオブジェクト
3-2.コンストラクタとデストラクタ
3-3.アクセス修飾子
3-4.継承
3-5.ポリモーフィズム
4.高度なC++機能
4-1.テンプレート
4-2.名前空間
4-3.標準テンプレートライブラリ
4-4.例外処理
5.練習問題
5-1.基本的な文法の練習問題
5-2.オブジェクト指向の練習問題
5-3.高度な機能の練習問題
6.まとめ
1.概要
C++は、システムプログラミングからゲーム開発、リアルタイムシステムなど幅広い用途で利用されるプログラミング言語です。本教材では、特に他のプログラミング言語の経験はあるが、C++に初めて触れるエンジニアを対象に、基本的な文法からオブジェクト指向プログラミング、そしてさらに高度な機能までをカバーします。
2.基本的な構文
2-1.コメント
コメントはコードの説明やメモとして使われ、コンパイラによって無視されます。
// これは一行コメントです
/*
これは
複数行にわたる
コメントです
*/
2-2.データ型と変数
C++には基本的なデータ型がいくつかあります。変数は特定のデータ型に属し、そのデータ型に合った値を保持します。
基本データ型
- int:整数型
- float:単精度浮動小数点型
- double:倍精度浮動小数点型
- char:文字型
- bool:真偽値型
変数の宣言と初期化
int a = 10; // 整数型の変数aを宣言し、10で初期化
float b = 5.5f; // 単精度浮動小数点型の変数bを宣言し、5.5で初期化
double c = 3.14; // 倍精度浮動小数点型の変数cを宣言し、3.14で初期化
char d = 'A'; // 文字型の変数dを宣言し、'A'で初期化
bool e = true; // 真偽値型の変数eを宣言し、trueで初期化
データ型の範囲
各データ型には取りうる値の範囲が決まっています。
- int:-2147483648 ~ 2147483647
- float:約3.4e-38 ~ 3.4e+38
- double:約1.7e-308 ~ 1.7e+308
- char:-128 ~ 127(符号付き)
- bool:true または false
2-3.演算子
演算子は変数や値に対して様々な操作を行うために使用されます。
算術演算子
- +:加算
- -:減算
- *:乗算
- /:除算
- %:剰余
int x = 10;
int y = 3;
int result;
result = x + y; // resultは13
result = x - y; // resultは7
result = x * y; // resultは30
result = x / y; // resultは3
result = x % y; // resultは1
比較演算子
- ==:等しい
- !=:等しくない
- <:小さい
- >:大きい
- <=:以下
- >=:以上
bool comparisonResult;
comparisonResult = (x == y); // false
comparisonResult = (x != y); // true
comparisonResult = (x < y); // false
comparisonResult = (x > y); // true
comparisonResult = (x <= y); // false
comparisonResult = (x >= y); // true
論理演算子
- &&:論理積(AND)
- ||:論理和(OR)
- !:論理否定(NOT)
bool result;
result = (true && false); // false
result = (true || false); // true
result = !true; // false
代入演算子
- =:代入
- +=:加算代入
- -=:減算代入
- *=:乗算代入
- /=:除算代入
- %=:剰余代入
int value = 10;
value += 5; // valueは15
value -= 3; // valueは12
value *= 2; // valueは24
value /= 4; // valueは6
value %= 5; // valueは1
2-4.制御文
制御文はプログラムの実行フローを制御するために使用されます
if文
if (条件) {
// 条件が真のとき実行されるコード
} else {
// 条件が偽のとき実行されるコード
}
elseif文
if (条件1) {
// 条件1が真のとき実行されるコード
} else if (条件2) {
// 条件2が真のとき実行されるコード
} else {
// どの条件も真でないとき実行されるコード
}
switch文
switch (変数) {
case 値1:
// 変数が値1のとき実行されるコード
break;
case 値2:
// 変数が値2のとき実行されるコード
break;
default:
// どのケースにも一致しないとき実行されるコード
break;
}
for文
for (初期化; 条件; 増分) {
// 繰り返し実行されるコード
}
while文
while (条件) {
// 条件が真の間繰り返し実行されるコード
}
do-while文
do {
// 繰り返し実行されるコード
} while (条件);
2-5.関数
関数は特定のタスクを実行するためのコードの集まりです。
関数の宣言と定義
// 関数の宣言(プロトタイプ)
戻り値の型 関数名(引数リスト);
// 関数の定義
戻り値の型 関数名(引数リスト) {
// 関数の本体
return 戻り値;
}
例:
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(5, 3);
std::cout << "Result: " << result << std::endl;
return 0;
}
関数のオーバーロード
同じ名前の関数を異なる引数リストで定義することが可能です。これを関数のオーバーロードと呼びます。
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int main() {
std::cout << add(1, 2) << std::endl; // 3
std::cout << add(1.5, 2.5) << std::endl; // 4.0
return 0;
}
引数のデフォルト値
関数の引数にデフォルト値を設定することができます。
int add(int a, int b = 10) {
return a + b;
}
int main() {
std::cout << add(5) << std::endl; // 15
std::cout << add(5, 5) << std::endl; // 10
return 0;
}
2-6.標準ライブラリ関数
C++には多くの組み込み関数があり、標準ライブラリとして提供されています。これらの関数を使うことで、プログラムを効率的にコーディングできます。ここでは、いくつかの基本的な標準ライブラリ関数を紹介し、その使用方法を解説します。
文字列操作
strlen
strlenは、Cスタイルの文字列(ヌル終端文字列)の長さを返します。C++11以降では、標準ライブラリのstd::stringクラスを使用する方が一般的ですが、Cスタイルの文字列を操作する場合には依然として重要な関数です。
#include <iostream>
#include <cstring>
int main() {
const char* str = "Hello, World!";
// 文字列の長さを取得
std::cout << "Length of the string: " << strlen(str) << std::endl;
return 0;
}
strcpy
strcpyは、Cスタイルの文字列を他の場所にコピーするために使用されます。
#include <iostream>
#include <cstring>
int main() {
char src[] = "Hello, World!";
char dest[50];
// 文字列をコピー
strcpy(dest, src);
std::cout << "Copied string: " << dest << std::endl;
return 0;
}
strcmp
strcmpは、2つのCスタイルの文字列を比較します。文字列が等しい場合は0、異なる場合は非ゼロを返します。
#include <iostream>
#include <cstring>
int main() {
const char* str1 = "Hello";
const char* str2 = "Hello";
const char* str3 = "World";
// str1とstr2を比較
if (strcmp(str1, str2) == 0) {
std::cout << "str1 and str2 are equal" << std::endl;
} else {
std::cout << "str1 and str2 are not equal" << std::endl;
}
// str1とstr3を比較
if (strcmp(str1, str3) == 0) {
std::cout << "str1 and str3 are equal" << std::endl;
} else {
std::cout << "str1 and str3 are not equal" << std::endl;
}
return 0;
}
tolowerとtoupper
tolowerは文字を小文字に変換し、toupperは文字を大文字に変換します。
#include <iostream>
#include <cctype>
int main() {
char ch = 'A';
// 文字を小文字に変換
std::cout << ch << " to lowercase is " << (char)tolower(ch) << std::endl;
ch = 'b';
// 文字を大文字に変換
std::cout << ch << " to uppercase is " << (char)toupper(ch) << std::endl;
return 0;
}
数学関数
数値計算に関する多くの組み込み関数が<cmath>ライブラリに含まれています。
sqrt
sqrtは平方根を計算します。
#include <iostream>
#include <cmath>
int main() {
double num = 9.0;
// 数の平方根を計算
std::cout << "Square root of " << num << " is " << sqrt(num) << std::endl;
return 0;
}
pow
powは累乗の計算を行います。
#include <iostream>
#include <cmath>
int main() {
double base = 2.0;
double exponent = 3.0;
// 累乗の計算
std::cout << base << "^" << exponent << " = " << pow(base, exponent) << std::endl;
return 0;
}
abs
absは整数の絶対値を返します。なお、浮動小数点数の場合はfabsを使用します。
#include <iostream>
#include <cstdlib> // absはstdlibに含まれています
int main() {
int num = -10;
// 整数の絶対値を計算
std::cout << "Absolute value of " << num << " is " << abs(num) << std::endl;
return 0;
}
乱数の生成
randとsrand
rand関数は、0からRAND_MAXまでのランダムな整数を返します。srand関数を使って乱数のシードを設定することで、生成される乱数を制御できます。
#include <iostream>
#include <cstdlib>
#include <ctime>
int main() {
// シード設定
srand(static_cast<unsigned int>(time(0)));
// ランダムな整数を5回生成
for (int i = 0; i < 5; ++i) {
std::cout << rand() % 100 << std::endl; // 0から99までのランダムな整数
}
return 0;
}
時刻関数
time
time関数は、エポック(1970年1月1日)からの経過秒数を返します。
#include <iostream>
#include <ctime>
int main() {
// 現在の時刻を取得
time_t currentTime = time(0);
std::cout << "Current time is " << currentTime << std::endl;
return 0;
}
localtimeとstrftime
これらの関数を使って、現在時刻を人間が読める形式にフォーマットします。
#include <iostream>
#include <ctime>
int main() {
// 現在の時刻を取得
time_t currentTime = time(0);
// tm構造体にローカル時間を格納
tm* localTime = localtime(¤tTime);
char buffer[80];
// 時刻を指定されたフォーマットに変換
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", localTime);
std::cout << "Formatted time: " << buffer << std::endl;
return 0;
}
メモリ操作
memcpy
memcpyはメモリブロックを別の場所にコピーします。
#include <iostream>
#include <cstring>
int main() {
char src[50] = "Hello, World!";
char dest[50];
// メモリをコピー
memcpy(dest, src, strlen(src) + 1);
std::cout << "Copied string: " << dest << std::endl;
return 0;
}
memset
memsetはメモリブロックを特定の値で埋めます。
#include <iostream>
#include <cstring>
int main() {
char str[50] = "Hello, World!";
// メモリを特定の値で埋める
memset(str, '*', 5); // 初めの5バイトを'*'で埋める
std::cout << "Modified string: " << str << std::endl;
return 0;
}
ファイル操作
fopenとfclose
fopenはファイルを開き、fcloseは開いたファイルを閉じます。
#include <iostream>
#include <cstdio>
int main() {
// ファイルを読み取り専用で開く
FILE *file = fopen("example.txt", "r");
if (file == nullptr) {
std::cerr << "File could not be opened." << std::endl;
return 1;
} else {
std::cout << "File opened successfully." << std::endl;
}
// ファイルを閉じる
fclose(file);
return 0;
}
fgetsとfputs
fgetsはファイルから文字列を読み取り、fputsは文字列をファイルに書き込みます。
#include <iostream>
#include <cstdio>
int main() {
// ファイルを読み取り専用で開く
FILE *file = fopen("example.txt", "r");
if (file == nullptr) {
std::cerr << "File could not be opened." << std::endl;
return 1;
}
char buffer[100];
// ファイルから文字列を読み取る
while (fgets(buffer, sizeof(buffer), file) != nullptr) {
std::cout << buffer;
}
// ファイルを閉じる
fclose(file);
return 0;
}
入出力操作
printfとscanf
printfはフォーマット付きの出力を行い、scanfはフォーマット付きの入力を行います。
#include <iostream>
#include <cstdio>
int main() {
int num;
// フォーマット付きで入力を求める
std::printf("Enter a number: ");
std::scanf("%d", &num);
// フォーマット付きで出力
std::printf("You entered: %d\n", num);
return 0;
}
sprintfとsscanf
sprintfはフォーマット付きの出力を文字列に格納し、sscanfは文字列からフォーマット付きの入力を行います。
#include <iostream>
#include <cstdio>
int main() {
char buffer[50];
int a = 10, b = 20;
// フォーマット付きで文字列に出力
sprintf(buffer, "a = %d, b = %d", a, b);
std::cout << buffer << std::endl; // "a = 10, b = 20"
int x, y;
// フォーマット付きで文字列から入力
sscanf(buffer, "a = %d, b = %d", &x, &y);
std::cout << "x = " << x << ", y = " << y << std::endl; // "x = 10, y = 20"
return 0;
}
データ変換
atoiとatof
atoiは文字列を整数に変換し、atofは文字列を浮動小数点数に変換します。
#include <iostream>
#include <cstdlib>
int main() {
const char* intStr = "123";
const char* floatStr = "123.456";
// 文字列を整数に変換
int intValue = atoi(intStr);
// 文字列を浮動小数点数に変換
double doubleValue = atof(floatStr);
std::cout << "Integer value: " << intValue << std::endl;
std::cout << "Double value: " << doubleValue << std::endl;
return 0;
}
strtol, strtoll, strtof, strtod, および strtold
これらの関数は、文字列をそれぞれlong, long long, float, double, および long doubleに変換します。
#include <iostream>
#include <cstdlib>
int main() {
const char* longStr = "1234567890";
const char* doubleStr = "123.456789";
// 文字列をlong型に変換
long longValue = strtol(longStr, nullptr, 10);
// 文字列をdouble型に変換
double doubleValue = strtod(doubleStr, nullptr);
std::cout << "Long value: " << longValue << std::endl;
std::cout << "Double value: " << doubleValue << std::endl;
return 0;
}
メモリブロック操作
mallocとfree
Cスタイルのメモリ管理にはmallocとfreeが使用されます。通常はC++のnewおよびdeleteを使用することが推奨されますが、Cスタイルのコードとの互換性のために知っておくと役立ちます。
#include <iostream>
#include <cstdlib> // mallocとfreeはstdlibに含まれています
int main() {
// 5つの整数用のメモリを割り当て
int* arr = (int*)malloc(5 * sizeof(int));
if (arr != nullptr) { // メモリ割り当ての成功を確認
for (int i = 0; i < 5; ++i) {
arr[i] = i * 2;
std::cout << arr[i] << " ";
}
std::cout << std::endl;
// メモリを解放
free(arr);
}
return 0;
}
3.オブジェクト指向プログラミング
3-1.クラスとオブジェクト
クラスはオブジェクトの設計図であり、オブジェクトはクラスのインスタンスです。
クラスの定義
class クラス名 {
public:
// パブリックメンバ関数と変数
型 メンバ関数();
型 メンバ変数;
private:
// プライベートメンバ関数と変数
型 メンバ関数();
型 メンバ変数;
};
例:
class Person {
public:
std::string name;
int age;
void introduce() {
std::cout << "Hello, my name is " << name << " and I am " << age << " years old." << std::endl;
}
};
int main() {
Person person;
person.name = "John";
person.age = 30;
person.introduce();
return 0;
}
3-2.コンストラクタとデストラクタ
コンストラクタはオブジェクトが生成されるときに呼ばれる特別なメンバ関数であり、デストラクタはオブジェクトが破棄されるときに呼ばれる特別なメンバ関数です。
コンストラクタ
class クラス名 {
public:
クラス名(); // デフォルトコンストラクタ
クラス名(引数リスト); // パラメータ付きコンストラクタ
};
デストラクタ
class クラス名 {
public:
~クラス名(); // デストラクタ
};
例:
class Person {
public:
std::string name;
int age;
// コンストラクタ
Person(std::string n, int a) : name(n), age(a) {}
// デストラクタ
~Person() {
std::cout << "Person object is being destroyed" << std::endl;
}
void introduce() {
std::cout << "Hello, my name is " << name << " and I am " << age << " years old." << std::endl;
}
};
int main() {
Person person("John", 30);
person.introduce();
return 0;
}
3-3.アクセス修飾子
アクセス修飾子はクラスメンバのアクセス範囲を制御します。
- public:どこからでもアクセス可能
- private:クラス内からのみアクセス可能
- protected:クラス内および派生クラスからアクセス可能
アクセス修飾子の例
class MyClass {
public:
int publicVar;
void publicMethod() {
std::cout << "Public method" << std::endl;
}
private:
int privateVar;
void privateMethod() {
std::cout << "Private method" << std::endl;
}
protected:
int protectedVar;
void protectedMethod() {
std::cout << "Protected method" << std::endl;
}
};
int main() {
MyClass obj;
obj.publicVar = 10; // OK
obj.publicMethod(); // OK
// obj.privateVar = 20; // エラー
// obj.privateMethod(); // エラー
// obj.protectedVar = 30; // エラー
// obj.protectedMethod(); // エラー
return 0;
}
3-4.継承
継承は既存のクラスを基に新しいクラスを定義する機能です。
基本クラス(親クラス)
class Base {
public:
void baseFunction() {
std::cout << "Base function" << std::endl;
}
};
派生クラス(子クラス)
class Derived : public Base {
public:
void derivedFunction() {
std::cout << "Derived function" << std::endl;
}
};
例:
int main() {
Derived derived;
derived.baseFunction();
derived.derivedFunction();
return 0;
}
3-5.ポリモーフィズム
ポリモーフィズム(多態性)は、同じインターフェースを通じて異なる実装を利用できるようにする機能です。特に、仮想関数を使用します。
仮想関数
class Base {
public:
virtual void show() {
std::cout << "Base class" << std::endl;
}
};
class Derived : public Base {
public:
void show() override {
std::cout << "Derived class" << std::endl;
}
};
例:
int main() {
Base* b;
Derived d;
b = &d;
b->show(); // "Derived class"が出力される
return 0;
}
4.高度なC++機能
4-1.テンプレート
テンプレートはデータ型に依存しない汎用的なコードを作成するための機能です。
関数テンプレート
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
std::cout << add<int>(2, 3) << std::endl; // 5
std::cout << add<double>(2.5, 3.5) << std::endl; // 6.0
return 0;
}
クラステンプレート
template <typename T>
class Container {
private:
T element;
public:
Container(T e) : element(e) {}
T getElement() {
return element;
}
};
int main() {
Container<int> intContainer(42);
Container<std::string> stringContainer("Hello");
std::cout << intContainer.getElement() << std::endl; // 42
std::cout << stringContainer.getElement() << std::endl; // Hello
return 0;
}
4-2.名前空間
名前空間(namespace)は識別子のスコープを指定するために使用されます。
namespace MyNamespace {
void myFunction() {
std::cout << "Hello from MyNamespace" << std::endl;
}
}
int main() {
MyNamespace::myFunction(); // Hello from MyNamespace
return 0;
}
4-3.標準テンプレートライブラリ
標準テンプレートライブラリ(STL)は、効率的なデータ操作を可能にするテンプレートクラスとテンプレート関数の集合です
std::sort
std::sortはSTL(標準テンプレートライブラリ)の一部であり、配列またはコンテナ内の要素をソートするために使用されます。
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
// ベクター(動的配列)の初期化
std::vector<int> vec = {5, 3, 1, 4, 2};
// 要素をソート
std::sort(vec.begin(), vec.end());
std::cout << "Sorted vector elements: ";
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
std::vector
std::vectorは動的配列を表し、要素の追加や削除、アクセスが効率的に行えます。
#include <iostream>
#include <vector>
int main() {
// ベクターの初期化
std::vector<int> vec = {1, 2, 3, 4, 5};
// 要素を追加
vec.push_back(6);
std::cout << "Vector elements: ";
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
// 最後の要素を削除
vec.pop_back();
std::cout << "After pop_back, vector elements: ";
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
std::map
std::mapはキーと値のペアを格納し、キーで検索できる連想コンテナです。
#include <iostream>
#include <map>
int main() {
// マップの初期化
std::map<std::string, int> myMap;
myMap["one"] = 1;
myMap["two"] = 2;
std::cout << "Map elements:" << std::endl;
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
// キー"one"を削除
myMap.erase("one");
std::cout << "After erase, map elements:" << std::endl;
for (const auto& pair : myMap) {
std::cout << pair.first << ": " << pair.second << std::endl;
}
return 0;
}
std::setの操作
std::setは重複を許さない、順序付けられた要素のコレクションです。
#include <iostream>
#include <set>
int main() {
// セットの初期化
std::set<std::string> mySet;
mySet.insert("apple");
mySet.insert("banana");
mySet.insert("apple"); // 重複するため追加されない
std::cout << "Set elements: ";
for (const auto& elem : mySet) {
std::cout << elem << " ";
}
std::cout << std::endl;
// 要素"banana"を削除
mySet.erase("banana");
std::cout << "After erase, set elements: ";
for (const auto& elem : mySet) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
イテレータ
イテレータはSTLコンテナを巡回するためのオブジェクトです。
#include <vector>
#include <iostream>
int main() {
// ベクターの初期化
std::vector<int> vec = {1, 2, 3, 4, 5};
// イテレータを使用してベクターの要素を巡回
for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
std::deque
std::deque(デック)は両端からの高速な追加と削除を提供するシーケンスコンテナです。
#include <iostream>
#include <deque>
int main() {
// デックの初期化
std::deque<int> deq = {1, 2, 3, 4, 5};
// 先頭に要素を追加
deq.push_front(0);
// 末尾に要素を追加
deq.push_back(6);
std::cout << "Deque elements: ";
for (int i : deq) {
std::cout << i << " ";
}
std::cout << std::endl;
// 先頭の要素を削除
deq.pop_front();
// 末尾の要素を削除
deq.pop_back();
std::cout << "After pop operations, deque elements: ";
for (int i : deq) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
std::stack
std::stackはLIFO(Last In First Out)データ構造を提供するコンテナアダプタです。
#include <iostream>
#include <stack>
int main() {
// スタックの初期化
std::stack<int> stk;
// 要素をスタックにプッシュ
stk.push(1);
stk.push(2);
stk.push(3);
std::cout << "Stack top element: " << stk.top() << std::endl;
// 要素をポップ
stk.pop();
std::cout << "After pop, stack top element: " << stk.top() << std::endl;
return 0;
}
std::queueの操作
std::queueはFIFO(First In First Out)データ構造を提供するコンテナアダプタです。
#include <iostream>
#include <queue>
int main() {
// キューの初期化
std::queue<int> que;
// 要素をキューにプッシュ
que.push(1);
que.push(2);
que.push(3);
std::cout << "Queue front element: " << que.front() << std::endl;
std::cout << "Queue back element: " << que.back() << std::endl;
// 要素をポップ
que.pop();
std::cout << "After pop, queue front element: " << que.front() << std::endl;
return 0;
}
std::priority_queue
std::priority_queueは優先度に基づいて要素を管理するヒープデータ構造です。
#include <iostream>
#include <queue>
#include <vector>
int main() {
// 優先度キューの初期化(デフォルトは最大ヒープ)
std::priority_queue<int> pq;
// 要素をプッシュ
pq.push(3);
pq.push(5);
pq.push(2);
pq.push(4);
std::cout << "Priority queue top element: " << pq.top() << std::endl;
// 要素をポップ
pq.pop();
std::cout << "After pop, priority queue top element: " << pq.top() << std::endl;
return 0;
}
std::listの操作
std::listは双方向リストを表し、リスト中の任意の位置での効率的な挿入と削除を提供します。
#include <iostream>
#include <list>
int main() {
// リストの初期化
std::list<int> lst = {1, 2, 3, 4, 5};
// 先頭に要素を追加
lst.push_front(0);
// 末尾に要素を追加
lst.push_back(6);
std::cout << "List elements: ";
for (int i : lst) {
std::cout << i << " ";
}
std::cout << std::endl;
// 先頭の要素を削除
lst.pop_front();
// 末尾の要素を削除
lst.pop_back();
std::cout << "After pop operations, list elements: ";
for (int i : lst) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
std::algorithmの便利な関数
std::find
std::findは範囲内での要素の検索を行います。見つからなかった場合は範囲の終わりを指すイテレータを返します。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
// ベクターの初期化
std::vector<int> vec = {1, 2, 3, 4, 5};
// 要素の検索
auto it = std::find(vec.begin(), vec.end(), 3);
if (it != vec.end()) {
std::cout << "Element found: " << *it << std::endl;
} else {
std::cout << "Element not found" << std::endl;
}
return 0;
}
std::reverse
std::reverseは範囲内の要素を反転させます。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
// ベクターの初期化
std::vector<int> vec = {1, 2, 3, 4, 5};
// 要素を反転
std::reverse(vec.begin(), vec.end());
std::cout << "Reversed vector elements: ";
for (int i : vec) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
std::accumulate
std::accumulateは範囲内の要素の累積を計算します。<numeric>ヘッダに含まれています。
#include <iostream>
#include <vector>
#include <numeric>
int main() {
// ベクターの初期化
std::vector<int> vec = {1, 2, 3, 4, 5};
// 要素の累積を計算
int sum = std::accumulate(vec.begin(), vec.end(), 0);
std::cout << "Sum of vector elements: " << sum << std::endl;
return 0;
}
4-4.例外処理
例外処理は、エラーが発生したときにプログラムの実行を中断し、適切な処理を行うための機能です。
try-catchブロック
例外が発生する可能性のあるコードをtryブロックに書き、例外が発生した場合の処理をcatchブロックに書きます。
#include <iostream>
#include <stdexcept>
int main() {
try {
// 例外が発生する可能性のあるコード
throw std::runtime_error("An error occurred");
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}
複数のcatchブロック
異なる型の例外をキャッチするために複数のcatchブロックを使用できます。
#include <iostream>
#include <stdexcept>
int main() {
try {
// 例外が発生する可能性のあるコード
throw std::out_of_range("Out of range error");
} catch (const std::out_of_range& e) {
std::cerr << "Out of range exception: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Other exception: " << e.what() << std::endl;
}
return 0;
}
独自の例外クラスの作成
独自の例外クラスを作成することもできます。
#include <iostream>
#include <exception>
class MyException : public std::exception {
public:
const char* what() const noexcept override {
return "My custom exception";
}
};
int main() {
try {
throw MyException();
} catch (const MyException& e) {
std::cerr << "Caught: " << e.what() << std::endl;
}
return 0;
}
4-5.スマートポインタ
スマートポインタはメモリ管理を簡素化するためのクラスです。C++11以降では、標準ライブラリが提供するスマートポインタを使用することが推奨されます。
std::unique_ptr
colerstd::unique_ptrは所有権を唯一保持するスマートポインタです。所有権は移動できますが、コピーはできません。
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> ptr = std::make_unique<int>(10);
std::cout << *ptr << std::endl; // 10
return 0;
}
std::shared_ptr
std::shared_ptrは複数の所有者を持つことができるスマートポインタです。所有者の数がゼロになると、リソースは自動的に解放されます。
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> ptr1 = std::make_shared<int>(20);
std::shared_ptr<int> ptr2 = ptr1;
std::cout << *ptr1 << std::endl; // 20
std::cout << ptr1.use_count() << std::endl; // 2 (ptr1 と ptr2 が同じメモリを共有している)
return 0;
}
std::weak_ptr
std::weak_ptrはstd::shared_ptrの循環参照を防ぐために使用されます。所有権は持ちませんが、std::shared_ptrの有効性を確認するために使用されます。
#include <iostream>
#include <memory>
int main() {
std::shared_ptr<int> sp = std::make_shared<int>(30);
std::weak_ptr<int> wp = sp;
if (auto spt = wp.lock()) { // std::shared_ptr に変換
std::cout << *spt << std::endl; // 30
} else {
std::cout << "Pointer is expired" << std::endl;
}
return 0;
}
5.練習問題
5-1.基本的な文法の練習問題
問題1
2つの整数を引数として受け取り、その和を返す関数を作成してください。
int add(int a, int b) {
// ここにコードを記述
}
int main() {
std::cout << add(2, 3) << std::endl; // 5
return 0;
}
解答例:
#include <iostream>
int add(int a, int b) {
return a + b;
}
int main() {
std::cout << add(2, 3) << std::endl; // 5
return 0;
}
問題2
if文を使って、入力された整数が正の数、負の数、またはゼロであるかを判定するプログラムを作成してください。
int main() {
int num;
std::cout << "Enter a number: ";
std::cin >> num;
// ここにコードを記述
return 0;
}
解答例:
#include <iostream>
int main() {
int num;
std::cout << "Enter a number: ";
std::cin >> num;
if (num > 0) {
std::cout << "The number is positive." << std::endl;
} else if (num < 0) {
std::cout << "The number is negative." << std::endl;
} else {
std::cout << "The number is zero." << std::endl;
}
return 0;
}
問題3
Cスタイルの文字列を使用し、以下の操作を行ってください。
- 文字列の長さを計算する
- 文字列を別の場所にコピーする
- 2つの文字列を比較する
#include <iostream>
#include <cstring>
int main() {
const char* str1 = "Hello, World!";
const char* str2 = "Hello, C++!";
char strCopy[50];
// TODO: 1. str1の長さを計算
// TODO: 2. str1をstrCopyにコピー
// TODO: 3. str1とstr2を比較
return 0;
}
解答例:
#include <iostream>
#include <cstring>
int main() {
// 初めに文字列を宣言
const char* str1 = "Hello, World!";
const char* str2 = "Hello, C++!";
char strCopy[50];
// 1. str1の長さを計算
size_t length = strlen(str1);
std::cout << "Length of str1: " << length << std::endl;
// 2. str1をstrCopyにコピー
strcpy(strCopy, str1);
std::cout << "Copied string: " << strCopy << std::endl;
// 3. str1とstr2を比較
int result = strcmp(str1, str2);
if (result == 0) {
std::cout << "str1 and str2 are equal" << std::endl;
} else {
std::cout << "str1 and str2 are not equal" << std::endl;
}
return 0;
}
5-2.オブジェクト指向の練習問題
問題3
クラスを使ってシンプルな銀行口座システムを作成してください。口座には残高を持ち、入金と出金のメソッドを持つようにしてください。
class BankAccount {
public:
// コンストラクタ
BankAccount(double initial_balance);
// メソッド
void deposit(double amount);
void withdraw(double amount);
double getBalance() const;
private:
double balance;
};
int main() {
BankAccount account(1000.0);
account.deposit(500.0);
account.withdraw(200.0);
std::cout << "Balance: " << account.getBalance() << std::endl;
return 0;
}
解答例:
#include <iostream>
class BankAccount {
private:
double balance;
public:
// コンストラクタ
BankAccount(double initial_balance) : balance(initial_balance) {}
// メソッド
void deposit(double amount) {
balance += amount;
}
void withdraw(double amount) {
if (amount <= balance) {
balance -= amount;
} else {
std::cout << "Insufficient balance" << std::endl;
}
}
double getBalance() const {
return balance;
}
};
int main() {
BankAccount account(1000.0);
account.deposit(500.0);
account.withdraw(200.0);
std::cout << "Balance: " << account.getBalance() << std::endl;
return 0;
}
問題4
継承を使って動物のクラスを作成し、犬と猫のクラスをそれぞれ派生させてください。それぞれのクラスに特有のメソッドを追加してください。
解答例:
class Animal {
public:
virtual void makeSound() const {
std::cout << "Some generic animal sound" << std::endl;
}
};
class Dog : public Animal {
public:
void makeSound() const override {
std::cout << "Woof!" << std::endl;
}
};
class Cat : public Animal {
public:
void makeSound() const override {
std::cout << "Meow!" << std::endl;
}
};
int main() {
Dog dog;
Cat cat;
dog.makeSound(); // Woof!
cat.makeSound(); // Meow!
return 0;
}
5-3.高度な機能の練習問題
問題5
テンプレートを使って、任意のデータ型の配列をソートする関数を作成してください。
解答例:
template <typename T>
void sortArray(T arr[], int size) {
// バブルソートの実装
for (int i = 0; i < size - 1; ++i) {
for (int j = 0; j < size - 1 - i; ++j) {
if (arr[j] > arr[j + 1]) {
std::swap(arr[j], arr[j + 1]);
}
}
}
}
int main() {
int intArray[] = {5, 2, 9, 1, 5, 6};
double doubleArray[] = {3.1, 2.4, 1.8, 4.1};
sortArray(intArray, 6);
sortArray(doubleArray, 4);
for (int i : intArray) {
std::cout << i << " ";
}
std::cout << std::endl;
for (double d : doubleArray) {
std::cout << d << " ";
}
std::cout << std::endl;
return 0;
}
問題6
例外処理を使って、0で除算した場合に例外を投げる関数を作成してください。
解答例:
double divide(double a, double b) {
if (b == 0) {
throw std::runtime_error("Division by zero");
}
return a / b;
}
int main() {
try {
std::cout << divide(10, 2) << std::endl; // 5
std::cout << divide(10, 0) << std::endl; // 例外が投げられる
} catch (const std::runtime_error& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
6.まとめ
本教材では、Cの基本的な文法からオブジェクト指向プログラミング、さらに高度な機能までを紹介しました。Cは強力で柔軟な言語ですが、その分学ぶべきことも多くあります。ここで紹介した内容をしっかりと理解し、実際にコードを書いてみることが上達の近道です。個人的には次に何か成果物を作ってみることをおすすめします。