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}