C++クラスとオブジェクトの基本

1. クラスとオブジェクト

1.1 クラスの定義

C++では、classキーワードを使ってクラスを定義する。以下はスタックを表すクラスの例である:

#include <iostream>
using namespace std;

class Stack {
private:
    int* _data;
    int _index;
    int _maxSize;

public:
    void push() {}
    void pop() {}
}; // セミコロンが必要

メンバー変数には慣習的に接頭辞(例: _m_)を付けることが多いが、これは言語仕様ではなくスタイルの問題である。

structでもクラスを定義できるが、デフォルトのアクセス指定子が異なる点に注意が必要(classprivatestructpublic)。

1.2 アクセス指定子

アクセス制御には以下の3種類がある:

  • public:外部からアクセス可能
  • protected:派生クラスからアクセス可能
  • private:クラス内部のみアクセス可能

アクセス指定子は宣言以降、次の指定子が現れるまで有効となる。

class Example {
private:
    int secret;        // private
public:
    void expose();     // public
protected:
    int internal;      // protected
};

1.3 クラススコープ

クラス外でメンバ関数を定義する際は、スコープ解決演算子::を使用する必要がある:

class Counter {
public:
    void reset(int value);
private:
    int _count;
};

void Counter::reset(int value) {
    _count = value; // this->_count と等価
}

2. オブジェクトのインスタンス化

2.1 インスタンス化の概念

クラスは設計図であり、実際のメモリ領域はオブジェクトとしてインスタンス化されたときに確保される:

class Point {
public:
    int x, y;
};

int main() {
    Point p;   // メモリが確保される
    p.x = 10;  // 利用可能
    return 0;
}

2.2 オブジェクトのサイズ

オブジェクトのサイズはメンバ変数のみに基づいて計算され、メンバ関数は含まれない。空のクラスでもサイズは1バイトとなる(識別子としての役割)。

メモリ配置はアライメントルールに従う:

class Aligned {
    char c;   // 1バイト → 4バイトにパディング
    int i;    // 4バイト
}; // 合計8バイト(x64環境)

3. thisポインタ

メンバ関数は暗黙的にthisポインタを受け取る。これは呼び出し元オブジェクトへのポインタであり、関数内で明示的に使用可能:

class Rectangle {
    int width, height;
public:
    void set(int w, int h) {
        this->width = w;
        this->height = h;
    }
};

thisポインタ自体は変更不能だが、指す内容は変更可能。通常はスタック上に格納される。

4. デフォルトメンバ関数

4.1 コンストラクタ

オブジェクト生成時に自動呼び出され、初期化を担当する特殊な関数:

class Timer {
    int _seconds;
public:
    Timer() : _seconds(0) {}                     // デフォルト
    Timer(int s) : _seconds(s) {}                // 引数あり
    Timer(int h, int m) : _seconds(h*3600 + m*60) {} // オーバーロード
};

ユーザーがコンストラクタを定義しない場合、コンパイラは無効なデフォルトコンストラクタを生成する(組み込み型は未初期化のまま)。

4.2 デストラクタ

オブジェクト破棄時に自動呼び出され、リソース解放を担当:

class Buffer {
    char* _ptr;
public:
    Buffer(size_t size) { _ptr = new char[size]; }
    ~Buffer() { delete[] _ptr; } // 必須
};

動的メモリやファイルハンドルなどを管理するクラスでは、明示的なデストラクタの実装が必須。

4.5 演算子オーバーロード

既存の演算子にクラス固有の意味を持たせることができる:

class Vector2D {
    double _x, _y;
public:
    Vector2D(double x, double y) : _x(x), _y(y) {}
    
    // メンバ関数版(左辺オペランドがthis)
    Vector2D operator+(const Vector2D& other) const {
        return Vector2D(_x + other._x, _y + other._y);
    }
    
    // 非メンバ関数版(対称性が必要な場合)
    friend bool operator==(const Vector2D& a, const Vector2D& b) {
        return a._x == b._x && a._y == b._y;
    }
};

以下の演算子はオーバーロード不可:., ::, sizeof, ?:, .*

constメンバ関数

const修飾メンバ関数はオブジェクトの状態を変更できない:

class Sensor {
    int _value;
public:
    int getValue() const { return _value; } // OK
    void setValue(int v) { _value = v; }    // NG in const context
};

この修飾により、thisポインタの型はconst Sensor* constとなる。

タグ: C++ クラス オブジェクト指向 コンストラクタ 演算子オーバーロード

5月15日 23:21 投稿