第1章:C++基礎とOOP概念

はじめに

CPP Module 00は、C++プログラミングへの第一歩です。このモジュールでは、CからC++への移行に必要な基礎概念を学びます。

本章では、名前空間、クラス、メンバ関数、iostream、static、constなど、C++の基本概念をコンピュータサイエンスの観点から解説します。

---

1. CからC++へ

1.1 なぜC++か

C++は1979年にBjarne StroustrupがC言語を拡張して開発しました。彼の目標は:

> "C with Classes: a language that provides Simula-like classes for C." > > (C with Classes: CにSimulaのようなクラスを提供する言語)

C++がCに追加した主な機能:

  • クラスとオブジェクト
  • 名前空間
  • 関数オーバーロード
  • 参照
  • 例外処理
  • テンプレート

1.2 コンパイルの違い

# C言語
gcc -Wall -Wextra -Werror program.c -o program

# C++
c++ -Wall -Wextra -Werror -std=c++98 program.cpp -o program

1.3 ファイル拡張子

| 言語 | ソースファイル | ヘッダファイル | |------|--------------|--------------| | C | .c | .h | | C++ | .cpp, .cxx, .cc | .hpp, .hxx, .h |

---

2. 名前空間(Namespace)

2.1 問題:名前の衝突

大規模プログラムでは、同じ名前の関数や変数が衝突する可能性があります:

/* C言語 - 問題のあるコード */
/* library_a.c */
void print() { printf("Library A\n"); }

/* library_b.c */
void print() { printf("Library B\n"); }  /* リンクエラー! */

2.2 解決:名前空間

C++では名前空間で識別子をグループ化できます:

namespace LibraryA {
    void print() { std::cout << "Library A" << std::endl; }
}

namespace LibraryB {
    void print() { std::cout << "Library B" << std::endl; }
}

int main() {
    LibraryA::print();  // Library A
    LibraryB::print();  // Library B
    return 0;
}

2.3 スコープ解決演算子(::)

::演算子は、名前空間やクラスのメンバにアクセスするために使用します:

// 名前空間のメンバにアクセス
std::cout << "Hello" << std::endl;

// グローバル名前空間へのアクセス
::globalFunction();

// クラスの静的メンバにアクセス
MyClass::staticMethod();

2.4 using宣言とusingディレクティブ

// using宣言:特定の名前を現在のスコープに導入
using std::cout;
using std::endl;

cout << "Hello" << endl;  // std:: なしで使用可能

// usingディレクティブ:名前空間全体を導入(推奨されない)
using namespace std;

cout << "Hello" << endl;  // 名前衝突のリスクがある

ベストプラクティス: ヘッダファイルではusing namespaceを使用しない。

2.5 標準名前空間 std

C++標準ライブラリの全ての機能はstd名前空間に定義されています:

std::string    // 文字列クラス
std::vector    // 動的配列
std::cout      // 標準出力
std::cin       // 標準入力
std::endl      // 改行とフラッシュ

---

3. クラスの基本

3.1 構造体からクラスへ

C言語の構造体はデータをグループ化するだけでしたが、C++のクラスはデータと関数を一つにまとめます:

/* C言語の構造体 */
struct Point {
    int x;
    int y;
};

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

// C++のクラス
class Point {
    int x;
    int y;
public:
    void print() {
        std::cout << "(" << x << ", " << y << ")" << std::endl;
    }
};

3.2 クラスの定義

class ClassName {
private:
    // プライベートメンバ(クラス外からアクセス不可)
    int privateData;

protected:
    // プロテクテッドメンバ(派生クラスからアクセス可能)
    int protectedData;

public:
    // パブリックメンバ(誰からでもアクセス可能)
    int publicData;
    void publicMethod();
};

3.3 structとclassの違い

C++ではstructclassはほぼ同じですが、デフォルトのアクセス指定子が異なります:

struct S {
    int x;  // デフォルトでpublic
};

class C {
    int x;  // デフォルトでprivate
};

3.4 カプセル化

David Parnasの情報隠蔽の原則に基づき、内部実装を隠してインターフェースだけを公開します:

class BankAccount {
private:
    double balance;  // 内部実装を隠蔽

public:
    // 公開インターフェース
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    bool withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }

    double getBalance() const {
        return balance;
    }
};

---

4. コンストラクタとデストラクタ

4.1 コンストラクタ

オブジェクト生成時に自動的に呼ばれる特殊な関数:

class Contact {
private:
    std::string firstName;
    std::string lastName;

public:
    // デフォルトコンストラクタ
    Contact() : firstName(""), lastName("") {
        std::cout << "Default constructor called" << std::endl;
    }

    // パラメータ付きコンストラクタ
    Contact(const std::string& first, const std::string& last)
        : firstName(first), lastName(last) {
        std::cout << "Parameterized constructor called" << std::endl;
    }
};

int main() {
    Contact c1;                     // デフォルトコンストラクタ
    Contact c2("John", "Doe");      // パラメータ付きコンストラクタ
    return 0;
}

4.2 初期化リスト

メンバを効率的に初期化するための構文:

class Example {
private:
    const int id;          // constメンバ
    std::string& ref;      // 参照メンバ
    std::string name;

public:
    // 初期化リストが必須
    Example(int i, std::string& r, const std::string& n)
        : id(i), ref(r), name(n) {
        // ここでは代入しかできない(初期化は終わっている)
    }
};

初期化リストを使う理由

  • constメンバの初期化
  • 参照メンバの初期化
  • 効率性(代入より速い)
  • 基底クラスの初期化
  • 4.3 デストラクタ

    オブジェクト破棄時に自動的に呼ばれる関数:

    class Resource {
    private:
        int* data;
    
    public:
        Resource(int size) {
            data = new int[size];
            std::cout << "Resource acquired" << std::endl;
        }
    
        ~Resource() {
            delete[] data;
            std::cout << "Resource released" << std::endl;
        }
    };
    
    int main() {
        {
            Resource r(100);  // コンストラクタ呼び出し
            // ... 使用 ...
        }  // スコープ終了:デストラクタ自動呼び出し
        return 0;
    }
    

    ---

    5. メンバ関数

    5.1 クラス内定義

    class Counter {
    private:
        int value;
    
    public:
        // クラス内で定義(インライン化される可能性)
        void increment() {
            value++;
        }
    
        int getValue() const {
            return value;
        }
    };
    

    5.2 クラス外定義

    // ヘッダファイル (Counter.hpp)
    class Counter {
    private:
        int value;
    
    public:
        void increment();
        int getValue() const;
    };
    
    // ソースファイル (Counter.cpp)
    void Counter::increment() {
        value++;
    }
    
    int Counter::getValue() const {
        return value;
    }
    

    5.3 constメンバ関数

    オブジェクトを変更しない関数にはconstを付けます:

    class Point {
    private:
        int x, y;
    
    public:
        // constメンバ関数(オブジェクトを変更しない)
        int getX() const { return x; }
        int getY() const { return y; }
    
        // 非constメンバ関数(オブジェクトを変更する)
        void setX(int newX) { x = newX; }
    };
    

    5.4 thisポインタ

    メンバ関数内で自分自身を指すポインタ:

    class Counter {
    private:
        int value;
    
    public:
        Counter& increment() {
            this->value++;
            return *this;  // 自分自身を返す(メソッドチェーン)
        }
    };
    
    // 使用例
    Counter c;
    c.increment().increment().increment();  // valueは3になる
    

    ---

    6. iostream

    6.1 出力(std::cout)

    #include <iostream>
    
    int main() {
        // 基本的な出力
        std::cout << "Hello, World!" << std::endl;
    
        // 複数の値を連結
        int age = 25;
        std::string name = "Alice";
        std::cout << "Name: " << name << ", Age: " << age << std::endl;
    
        // 改行
        std::cout << "Line 1" << std::endl;  // 改行 + フラッシュ
        std::cout << "Line 2\n";             // 改行のみ
    
        return 0;
    }
    

    6.2 入力(std::cin)

    #include <iostream>
    #include <string>
    
    int main() {
        // 整数の入力
        int number;
        std::cout << "Enter a number: ";
        std::cin >> number;
    
        // 文字列の入力(スペースまで)
        std::string word;
        std::cout << "Enter a word: ";
        std::cin >> word;
    
        // 行全体の入力
        std::string line;
        std::cin.ignore();  // 前の改行を消費
        std::cout << "Enter a line: ";
        std::getline(std::cin, line);
    
        return 0;
    }
    

    6.3 フォーマット

    #include <iostream>
    #include <iomanip>
    
    int main() {
        // 幅指定
        std::cout << std::setw(10) << 42 << std::endl;     // "        42"
    
        // 右寄せ/左寄せ
        std::cout << std::setw(10) << std::right << 42 << std::endl;  // "        42"
        std::cout << std::setw(10) << std::left << 42 << std::endl;   // "42        "
    
        // 埋め文字
        std::cout << std::setfill('0') << std::setw(5) << 42 << std::endl;  // "00042"
    
        return 0;
    }
    

    ---

    7. staticメンバ

    7.1 静的メンバ変数

    クラスの全インスタンスで共有される変数:

    class Counter {
    private:
        static int count;  // 宣言のみ
        int id;
    
    public:
        Counter() : id(++count) {
            std::cout << "Object " << id << " created" << std::endl;
        }
    
        static int getCount() {
            return count;
        }
    };
    
    // 定義(クラス外で必要)
    int Counter::count = 0;
    
    int main() {
        Counter c1;  // Object 1 created
        Counter c2;  // Object 2 created
        Counter c3;  // Object 3 created
    
        std::cout << "Total: " << Counter::getCount() << std::endl;  // 3
        return 0;
    }
    

    7.2 静的メンバ関数

    インスタンスなしで呼び出せる関数:

    class Math {
    public:
        static int add(int a, int b) {
            return a + b;
        }
    
        static int abs(int x) {
            return x < 0 ? -x : x;
        }
    };
    
    int main() {
        int sum = Math::add(3, 5);  // 8
        int absolute = Math::abs(-10);  // 10
        return 0;
    }
    

    注意: 静的メンバ関数内ではthisポインタを使用できません。

    ---

    8. const修飾子

    8.1 const変数

    const int MAX_SIZE = 100;  // 変更不可
    
    // MAX_SIZE = 200;  // エラー!
    

    8.2 constポインタ

    int x = 10;
    int y = 20;
    
    const int* ptr1 = &x;   // 指す先がconst
    // *ptr1 = 30;          // エラー
    ptr1 = &y;              // OK
    
    int* const ptr2 = &x;   // ポインタ自体がconst
    *ptr2 = 30;             // OK
    // ptr2 = &y;           // エラー
    
    const int* const ptr3 = &x;  // 両方const
    // *ptr3 = 30;          // エラー
    // ptr3 = &y;           // エラー
    

    8.3 const参照

    void printValue(const std::string& str) {
        std::cout << str << std::endl;
        // str = "modified";  // エラー:変更不可
    }
    

    ---

    まとめ

    本章で学んだこと:

  • CからC++への移行: コンパイル方法、ファイル拡張子
  • 名前空間: 名前の衝突を避ける機能
  • クラスの基本: カプセル化、アクセス制御
  • コンストラクタ/デストラクタ: オブジェクトのライフサイクル
  • メンバ関数: クラスの振る舞いを定義
  • iostream: C++スタイルの入出力
  • staticメンバ: クラス共有のデータと関数
  • const: 不変性の保証

次章では、これらの概念を使ってCPP Module 00の課題を実装します。