コンテンツにスキップ

C++/CからC++への移行

出典: フリー教科書『ウィキブックス(Wikibooks)』
< C++

はじめに[編集]

C++は、C言語の強力な基盤の上にオブジェクト指向プログラミングやその他の高レベルな機能を追加した言語です。特にモダンC++(C++11以降の標準)は、コードの安全性、効率性、生産性を向上させる数多くの新機能を提供しています。この章では、C言語のプログラマがC++へ移行する際に役立つ情報を提供し、C++の利点とその新機能を紹介します。

基本的な違い[編集]

C++では、プログラムの構造化や名前の衝突を避けるために、名前空間が導入されています。C++の標準ライブラリは、すべてstdという名前空間に含まれています。

#include <iostream>

namespace MyNamespace {
    void hello() {
        std::cout << "Hello from MyNamespace!" << std::endl;
    }
}

auto main() -> int {
    MyNamespace::hello();
    return 0;
}

この例では、MyNamespaceという名前空間を定義し、その中にhello関数を配置しています。これにより、名前の衝突を避けることができます。

プログラミングスタイルの変化[編集]

C++はオブジェクト指向プログラミング(OOP)をサポートし、データと関数をクラスという単位でまとめることができます。これにより、コードの再利用性や保守性が向上します。

class MyClass {
  public:
    void sayHello() {
        std::cout << "Hello, World!" << std::endl;
    }
};

auto main() -> int {
    MyClass obj;
    obj.sayHello();
    return 0;
}

この例では、MyClassというクラスを定義し、その中にsayHelloというメンバ関数を持たせています。main関数では、MyClassのインスタンスobjを作成し、sayHelloを呼び出しています。

メモリ管理とスマートポインタ[編集]

C言語では、mallocfreeを使って手動でメモリ管理を行いますが、C++ではnewdeleteを使って動的メモリを管理します。さらに、C++11以降ではスマートポインタが導入され、メモリ管理が簡単かつ安全になりました。

#include <memory>

void useSmartPointer() {
    std::unique_ptr<int> p1(new int(5));
    std::shared_ptr<int> p2 = std::make_shared<int>(10);
    // メモリは自動的に解放される
}

std::unique_ptrは単一の所有権を持つポインタで、std::shared_ptrは複数の所有権を共有するポインタです。これにより、メモリリークやダングリングポインタのリスクが減少します。

標準ライブラリの利用[編集]

C++の標準ライブラリ(STL: Standard Template Library)は、データ構造やアルゴリズムを提供します。これにより、効率的で再利用可能なコードを簡単に書くことができます。

#include <vector>
#include <algorithm>
#include <iostream>

auto main() -> int {
    std::vector<int> vec = {1, 5, 6, 4, 3};
    std::sort(vec.begin(), vec.end());
    for (int n : vec) {
        std::cout << n << " ";
    }
    return 0;
}

この例では、std::vectorを使って整数のリストを作成し、std::sortでソートしています。

関数の強化[編集]

C++では、関数のオーバーロードやデフォルト引数がサポートされており、同じ名前の関数を異なる引数リストで定義できます。また、C++11以降ではラムダ式が導入され、簡潔に関数を定義できます。

#include <iostream>
#include <functional>

void print(int x) {
    std::cout << "int: " << x << std::endl;
}

void print(double x) {
    std::cout << "double: " << x << std::endl;
}

auto main() -> int {
    print(10);       // int: 10
    print(10.5);     // double: 10.5

    auto lambda = [](int x) { return x * 2; };
    std::cout << lambda(5) << std::endl;  // 10
    return 0;
}

型安全と型推論[編集]

C++では型安全性が重要視されており、autoキーワードを使ってコンパイラに型を推論させることができます。また、decltypeを使って式の型を取得できます。

#include <iostream>

auto main() -> int {
    auto x = 5;        // int
    auto y = 5.5;      // double
    decltype(x) z = 10; // int

    std::cout << x << ", " << y << ", " << z << std::endl;
    return 0;
}

モダンC++の機能[編集]

モダンC++の特徴的な機能として、ムーブセマンティクスと右辺値参照があります。これにより、リソースの効率的な管理が可能となります。

#include <iostream>
#include <vector>

void processVector(std::vector<int>&& vec) {
    std::cout << "Processing vector of size " << vec.size() << std::endl;
}

auto main() -> int {
    std::vector<int> myVec = {1, 2, 3, 4, 5};
    processVector(std::move(myVec));  // ムーブセマンティクスを利用
    return 0;
}

この例では、std::moveを使ってmyVecを右辺値参照として渡し、リソースを効率的に移動しています。

例外処理[編集]

C++では例外処理を利用して、エラーが発生した際にプログラムの安全な終了を図ることができます。

#include <iostream>
#include <stdexcept>

void mayThrow(bool shouldThrow) {
    if (shouldThrow) {
        throw std::runtime_error("An error occurred!");
    }
}

auto main() -> int {
    try {
        mayThrow(true);
    } catch (const std::runtime_error& e) {
        std::cerr << "Caught an exception: " << e.what() << std::endl;
    }
    return 0;
}

この例では、mayThrow関数がstd::runtime_errorを投げ、それをtry-catchブロックで捕捉しています。

コンパイル時のプログラム[編集]

テンプレートプログラミングにより、型に依存しない汎用的な関数やクラスを定義できます。また、constexprを使ってコンパイル時に評価される定数式を定義できます。

#include <iostream>

template <typename T>
T add(T a, T b) {
    return a + b;
}

constexpr int factorial(int n) {
    return (n <= 1) ? 1 : (n * factorial(n - 1));
}

auto main() -> int {
    std::cout << "Sum: " << add(5, 3) << std::endl;  // Sum: 8
    std::cout << "Factorial of 5: " << factorial(5) << std::endl;  // Factorial of 5: 120
    return 0;
}

C++11以降の新機能[編集]

C++11以降の標準では、多くの新機能が追加されました。例えば、範囲ベースのforループ、構造化束縛、コンセプトなどがあります。

#include <iostream>
#include <vector>

auto main() -> int {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    for (int n : vec) { // 範囲ベースのforループ
        std::cout << n << " ";
    }
    std::cout << std::endl;

    auto [a, b] = std::make_pair(1, 2); // 構造化束縛
    std::cout << "a: " << a << ", b: " << b << std::endl;

    return 0;
}

移行の実例[編集]

CのコードをC++に変換する具体例を示します。以下のCコードをC++に変換します。

Cコード
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int x;
    int y;
} Point;

void printPoint(Point* p) {
    printf("Point(%d, %d)\n", p->x, p->y);
}

int main() {
    Point* p = (Point*)malloc(sizeof(Point));
    p->x = 10;
    p->y = 20;
    printPoint(p);
    free(p);
    return 0;
}
C++コード
#include <iostream>
#include <memory>

class Point {
  public:
    int x, y;
    Point(int x, int y) : x{x}, y{y} {}
    void printPoint() const {
        std::cout << "Point(" << x << ", " << y << ")" << std::endl;
    }
};

auto main() -> int {
    auto p = std::make_unique<Point>(10, 20);
    p->printPoint();
    return 0;
}

総まとめと次のステップ[編集]

この章では、CからC++への移行に必要な基本的な概念とモダンC++の主要な機能を紹介しました。これらの知識を基に、さらに高度なC++の技術やデザインパターンを学び、より効率的で保守性の高いプログラムを作成することを目指しましょう。