C++/C++特有の概念
はじめに
編集C++は、C言語を基にしたプログラミング言語であり、オブジェクト指向プログラミング(OOP)やジェネリックプログラミング、メモリ管理の高度な機能など、数多くの新しい概念と機能を取り入れています。本章では、C++に特有のこれらの機能について詳しく説明し、C言語との違いを理解することを目的とします。また、2020年12月に規格出版されたC++20規格で追加された機能も紹介します。
オブジェクト指向プログラミング (OOP)
編集クラスとオブジェクト
編集C++の中心となる概念の一つがオブジェクト指向プログラミング(OOP)です。OOPは、データとその操作を一つの単位(オブジェクト)としてまとめる手法で、C++ではこれをクラスとして定義します。
class Dog { public: std::string name; int age; void bark() { std::cout << "Woof!" << std::endl; } }; auto main() -> int { Dog myDog; myDog.name = "Rex"; myDog.age = 3; myDog.bark(); return 0; }
コンストラクタとデストラクタ
編集クラスのオブジェクトが生成されるときに呼び出されるのがコンストラクタ、オブジェクトが破棄されるときに呼び出されるのがデストラクタです。
class Cat { public: Cat() { std::cout << "Cat is created" << std::endl; } ~Cat() { std::cout << "Cat is destroyed" << std::endl; } };
継承
編集C++では既存のクラスを基に新しいクラスを作成することができます。これを継承と言います。
class Animal { public: void eat() { std::cout << "Eating" << std::endl; } }; class Dog : public Animal { public: void bark() { std::cout << "Woof!" << std::endl; } };
カプセル化と情報隠蔽
編集クラスはデータとメソッドをまとめてカプセル化し、内部の実装を隠蔽します。これにより、データの保護とモジュール性が向上します。
class Person { private: std::string name; public: void setName(std::string newName) { name = newName; } std::string getName() { return name; } };
演算子のオーバーロード
編集C++では、既存の演算子を特定のクラスに対して再定義することができます。これを演算子オーバーロードと言います。
class Complex { public: int real, imag; Complex operator + (const Complex& other) { Complex result; result.real = this->real + other.real; result.imag = this->imag + other.imag; return result; } };
テンプレート
編集関数テンプレートとクラステンプレート
編集テンプレートは、データ型に依存しない汎用的なプログラムを作成するための手法です。
template <typename T> T add(T a, T b) { return a + b; }
テンプレートの特化と部分特化
編集テンプレートの特化により、特定の型に対して特別な実装を提供できます。
template <> std::string add(std::string a, std::string b) { return a + " " + b; }
Variadic Templates (可変長テンプレート)
編集可変長テンプレートは、複数の引数を取るテンプレートを作成するための機能です。
template <typename... Args> void print(Args... args) { (std::cout << ... << args) << std::endl; }
Concepts (C++20)
編集コンセプトは、テンプレートの制約を記述するための新しい機能です。
template <typename T> concept Addable = requires(T a, T b) { a + b; }; template <Addable T> T add(T a, T b) { return a + b; }
標準ライブラリ (STL)
編集コンテナ
編集標準テンプレートライブラリ(STL)は、様々なデータ構造(コンテナ)を提供します。
#include <vector> #include <iostream> std::vector<int> vec = {1, 2, 3, 4, 5}; for (auto i : vec) { std::cout << i << " "; }
イテレータ
編集イテレータは、コンテナの要素にアクセスするためのオブジェクトです。
for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); it++) { std::cout << *it << " "; }
アルゴリズム
編集STLは、様々なアルゴリズムを提供します。
#include <algorithm> std::sort(vec.begin(), vec.end());
ファンクタとラムダ式
編集STLは、関数オブジェクト(ファンクタ)やラムダ式を利用することができます。
std::for_each(vec.begin(), vec.end(), [](int n) { std::cout << n << " "; });
スマートポインタ
編集C++はメモリ管理を効率化するためのスマートポインタを提供します。
std::unique_ptr
編集std::unique_ptr<int> p = std::make_unique<int>(10);
std::shared_ptr
編集std::shared_ptr<int> p1 = std::make_shared<int>(10); std::shared_ptr<int> p2 = p1;
std::weak_ptr
編集std::weak_ptr<int> wp = p1;
メモリ管理のベストプラクティス
編集スマートポインタを使用することで、メモリリークやダングリングポインタを防ぐことができます。
例外処理
編集例外の基本
編集C++は、エラー処理のために例外機構を提供します。
try { throw std::runtime_error("Error occurred"); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; }
標準例外クラス
編集標準ライブラリには、多くの例外クラスが定義されています。
カスタム例外クラスの作成
編集独自の例外クラスを定義することもできます。
class MyException : public std::exception { public: const char* what() const noexcept override { return "My custom exception"; } };
ラムダ式と関数オブジェクト
編集ラムダ式の基本
編集ラムダ式は、無名の関数オブジェクトを簡潔に記述するための構文です。
auto add = [](int a, int b) { return a + b; }; std::cout << add(2, 3) << std::endl;
キャプチャとその使い方
編集ラムダ式は、周囲の変数をキャプチャすることができます。
int x{10}; auto f = [x](int y) { return x + y; }; std::cout << f(5) << std::endl;
std::functionとfunctionオブジェクト
編集std::functionは、関数オブジェクトを格納するための汎用的なクラスです。
std::function<int(int, int)> func = add; std::cout << func(2, 3) << std::endl;
名前空間
編集名前空間の基本
編集名前空間は、名前の衝突を避けるための機構です。
namespace MyNamespace { void myFunction() { std::cout << "Hello, World!" << std::endl; } }
名前空間の入れ子
編集名前空間は入れ子にすることもできます。
namespace MyNamespace { namespace NestedNamespace { void nestedFunction() { std::cout << "Nested namespace function" << std::endl; } } }
名前空間エイリアス
編集長い名前空間を短縮するためにエイリアスを使うことができます。
namespace MN = MyNamespace::NestedNamespace; MN::nestedFunction();
C++11以降の新機能
編集autoと型推論
編集autoキーワードは、変数の型を自動的に推論します。
auto x = 10; // xはint型
範囲ベースforループ
編集範囲ベースのforループは、コンテナや配列を簡潔に操作するための構文です。
for (int n : vec) { std::cout << n << " "; }
nullptrと型安全なnullポインタ
編集nullptrは、型安全なnullポインタを提供します。
int* p = nullptr;
静的アサーション (static_assert)
編集static_assertは、コンパイル時に条件をチェックします。
static_assert(sizeof(int) == 4, "intは4バイトである必要があります");
初期化リスト
編集初期化リストを使ってオブジェクトを初期化できます。
std::vector<int> v = {1, 2, 3, 4, 5};
ムーブセマンティクスと右辺値参照
編集ムーブセマンティクスは、リソースの所有権を効率的に移動するための機構です。
std::vector<int> v1 = {1, 2, 3}; std::vector<int> v2 = std::move(v1);
C++14の追加機能
編集return型推論
編集関数の戻り値の型を自動的に推論します。
auto add(int a, int b) { return a + b; }
二重角かっこ (二重括弧)
編集二重角かっこは、テンプレートのネストを簡潔に記述します。
std::vector<std::vector<int>> matrix;
std::make_unique
編集std::make_uniqueは、std::unique_ptrを作成するための便利関数です。
auto p = std::make_unique<int>(42);
変数テンプレート
編集変数テンプレートを使って、定数をテンプレート化できます。
template<typename T> constexpr T pi = T(3.1415926535897932385);
C++17の追加機能
編集if文とswitch文の初期化
編集if文やswitch文で変数の初期化が可能になりました。
if (int x = getValue(); x > 0) { std::cout << "Positive" << std::endl; }
構造化束縛
編集構造化束縛を使って、複数の変数を一度に宣言できます。
auto [a, b] = std::pair<int, int>(1, 2);
fold式
編集可変長テンプレートを使った簡潔な演算が可能になりました。
template<typename... Args> auto sum(Args... args) { return (args + ...); }
std::optional, std::variant, std::any
編集新しいユーティリティクラスが追加されました。
std::optional<int> opt = 5; if (opt) { std::cout << *opt << std::endl; }
C++20の追加機能
編集コルーチン
編集コルーチンを使って、非同期処理を簡潔に記述できます。
#include <coroutine> struct Generator { struct promise_type { int value_; std::suspend_always yield_value(int value) { value_ = value; return {}; } Generator get_return_object() { return Generator{ this }; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() { std::exit(1); } }; promise_type* h_; explicit Generator(promise_type* h) : h_(h) {} bool resume() { return !h_->value_; } int value() const { return h_->value_; } }; Generator gen() { for (int i = 0; i < 3; ++i) { co_yield i; } } auto main() -> int { auto g = gen(); while (g.resume()) { std::cout << g.value() << std::endl; } return 0; }
コンセプト (Concepts)
編集コンセプトは、テンプレートの制約を明示的に記述します。
範囲ライブラリ (Ranges)
編集範囲ライブラリを使って、より直感的な操作が可能です。
#include <ranges> auto view = std::views::iota(1, 10); for (int i : view) { std::cout << i << " "; }
三方比較演算子 (Spaceship operator)
編集三方比較演算子を使って、比較演算を簡潔に記述できます。
std::partial_ordering cmp = 1 <=> 2; if (cmp == std::partial_ordering::less) { std::cout << "1 is less than 2" << std::endl; }
モジュール (Modules)
編集モジュールは、コードの分割と再利用を促進します。
export module MyModule; export void myFunction() { std::cout << "Hello, Modules!" << std::endl; }
フォーマットライブラリ (std::format)
編集std::formatを使って、文字列のフォーマットを簡潔に行えます。
#include <format> std::cout << std::format("Hello, {}!", "world") << std::endl;
非同期プログラミングと並行処理
編集std::thread
編集スレッドを使って並行処理を実現します。
std::thread t([] { std::cout << "Hello from thread" << std::endl; }); t.join();
std::asyncとfuture
編集非同期タスクの実行結果を取得します。
auto future = std::async([] { return 42; }); std::cout << future.get() << std::endl;
ミューテックスとロック
編集複数のスレッド間で共有リソースを安全に操作します。
std::mutex mtx; mtx.lock(); std::cout << "Locked" << std::endl; mtx.unlock();
原子操作とメモリモデル
編集低レベルのスレッド間同期を提供します。
std::atomic<int> counter(0); counter++;
終わりに
編集本章では、C++の特有の概念と最新機能について紹介しました。これらの機能を理解し、効果的に活用することで、より高度で効率的なプログラムを作成できるようになります。さらなる学習には、公式ドキュメントや専門書、オンラインのコミュニティを活用すると良いでしょう。また、実践的なプロジェクトに取り組むことで、知識を深め、スキルを向上させることができます。
{{Nav}