フリーキーズ | 独学プログラミング

C++クラス入門!オブジェクト指向を理解する最初の一歩

最終更新日
リンドくん

リンドくん

先生、C++を勉強し始めたんですけど、「クラス」っていう概念がよくわからないんです...

たなべ

たなべ

C++のクラスか!確かに最初は戸惑うよね。でも現実世界の「設計図」みたいなものだと考えると理解しやすいんだよ。
今日はクラスの基本から、実際にコードを書くまでしっかり説明するね!

プログラミングを学んでいると、必ず出会うのが「オブジェクト指向」という概念です。
そして、C++でオブジェクト指向を理解する上で避けて通れないのがクラス(class)という仕組みです。

「クラスって何?」「なぜ必要なの?」「どうやって使うの?」そんな疑問を持つプログラミング学習者の方は多いのではないでしょうか。
この記事では、C++のクラスについて、初心者の方でも理解できるよう段階的に解説していきます。

クラスとは

リンドくん

リンドくん

先生、そもそもクラスって何のために使うんですか?

たなべ

たなべ

クラスは「もの」を表現するための設計図なんだ。
例えば、車を作るときに設計図があるように、プログラムでも「車」というものを表現するためにクラスを使うんだよ。

クラスの基本的な考え方

C++におけるクラスとは、データ(変数)と機能(関数)をひとまとめにした設計図のことです。
これにより、現実世界の「もの」をプログラム上で表現できるようになります。

従来のプログラミングでは、データと処理が別々に管理されていました。
しかし、クラスを使うことで以下のようなメリットが得られます。

  • データと処理を一箇所にまとめられる → 管理しやすくなる
  • 同じような処理を繰り返し使える → コードの再利用性が向上
  • 複雑なシステムを分割して考えられる → 大規模開発が容易になる

身近な例で理解するクラス

例えば、「学生」というクラスを考えてみましょう。
学生には以下のような特徴があります。

データ(属性)

  • 名前
  • 学年
  • 成績

機能(メソッド)

  • 勉強する
  • テストを受ける
  • 成績を確認する

このように、「学生」という概念をデータと機能の組み合わせとして表現するのがクラスの基本的な考え方です。
そして、このクラスという設計図から実際に作られた「田中さん」や「佐藤さん」といった具体的な学生がオブジェクト(インスタンス)と呼ばれます。

C++でのクラス定義の基本文法

リンドくん

リンドくん

実際にC++でクラスを書くにはどうするんですか?

たなべ

たなべ

基本的な書き方は意外とシンプルなんだよ!
まずは簡単な例から始めてみようか。classキーワードを使って定義するんだ。

基本的なクラス定義

C++でクラスを定義する基本的な文法は以下の通りです。

class クラス名
{
private:
    // プライベートメンバ(外部からアクセスできない)
    
public:
    // パブリックメンバ(外部からアクセスできる)
    
protected:
    // プロテクテッドメンバ(継承で使用)
};

実際に簡単な「学生」クラスを定義してみましょう。

#include <iostream>
#include <string>

class Student
{
private:
    std::string name;    // 名前
    int grade;          // 学年
    int score;          // 成績

public:
    // コンストラクタ(オブジェクト作成時に呼ばれる)
    Student(std::string n, int g)
    {
        name = n;
        grade = g;
        score = 0;
    }
    
    // 勉強する機能
    void study()
    {
        std::cout << name << "が勉強しています。" << std::endl;
        score += 10;
    }
    
    // 成績を表示する機能
    void showScore()
    {
        std::cout << name << "の成績: " << score << "点" << std::endl;
    }
};

アクセス指定子の役割

クラスにはアクセス指定子という重要な概念があります。

  • private: クラス内部からのみアクセス可能
  • public: どこからでもアクセス可能
  • protected: そのクラスと継承したクラスからアクセス可能

この仕組みにより、**データの隠蔽(カプセル化)**が実現できます。
外部から直接変更されては困るデータをprivateにすることで、安全性を保てるのです。

クラスを使用する方法

定義したクラスを実際に使ってみましょう。

int main()
{
    // Studentオブジェクトの作成
    Student tanaka("田中太郎", 2);
    Student sato("佐藤花子", 3);
    
    // メソッドの呼び出し
    tanaka.study();
    tanaka.showScore();
    
    sato.study();
    sato.study();  // 2回勉強
    sato.showScore();
    
    return 0;
}

このコードを実行すると、以下のような出力が得られます。

田中太郎が勉強しています。
田中太郎の成績: 10点
佐藤花子が勉強しています。
佐藤花子が勉強しています。
佐藤花子の成績: 20点

同じクラスから複数のオブジェクトを作成でき、それぞれが独立したデータを持っていることがわかりますね。

コンストラクタとデストラクタの基本

リンドくん

リンドくん

さっきのコードで「コンストラクタ」って出てきましたけど、これは何ですか?

たなべ

たなべ

コンストラクタはオブジェクトが作られるときに自動的に呼ばれる特別な関数なんだ。
オブジェクトの初期設定を行うために使うんだよ。対になる「デストラクタ」もあるんだ。

コンストラクタの役割と書き方

コンストラクタは、オブジェクトが生成される際に自動的に実行される特別なメンバ関数です。
主な役割は以下の通りです。

  • オブジェクトの初期化
  • 必要なメモリの確保
  • 初期設定の実行

コンストラクタの特徴は以下の通りです。

  • クラス名と同じ名前を持つ
  • 戻り値の型を指定しない
  • オブジェクト生成時に自動的に呼び出される

様々なコンストラクタのパターン

class Car
{
private:
    std::string brand;
    std::string model;
    int year;
    double fuel;

public:
    // デフォルトコンストラクタ
    Car()
    {
        brand = "Unknown";
        model = "Unknown";
        year = 2000;
        fuel = 0.0;
        std::cout << "デフォルトコンストラクタが呼ばれました" << std::endl;
    }
    
    // パラメータ付きコンストラクタ
    Car(std::string b, std::string m, int y)
    {
        brand = b;
        model = m;
        year = y;
        fuel = 50.0;  // 満タンでスタート
        std::cout << brand << " " << model << "が作成されました" << std::endl;
    }
    
    // 初期化リストを使ったコンストラクタ(推奨)
    Car(std::string b, std::string m, int y, double f) : brand(b), model(m), year(y), fuel(f)
    {
        std::cout << "詳細設定で" << brand << "が作成されました" << std::endl;
    }
    
    // 車の情報を表示
    void showInfo()
    {
        std::cout << year << "年式 " << brand << " " << model 
                  << " (燃料: " << fuel << "L)" << std::endl;
    }
    
    // デストラクタ
    ~Car()
    {
        std::cout << brand << " " << model << "が破棄されました" << std::endl;
    }
};

実際の使用例

int main()
{
    // 様々な方法でオブジェクトを作成
    Car car1;                                    // デフォルトコンストラクタ
    Car car2("Toyota", "Prius", 2023);          // パラメータ付き
    Car car3("Honda", "Civic", 2022, 30.5);     // 詳細設定
    
    // 情報を表示
    car1.showInfo();
    car2.showInfo();
    car3.showInfo();
    
    return 0;
    // ここでデストラクタが自動的に呼ばれる
}

デストラクタの重要性

デストラクタはオブジェクトが破棄される際に自動的に呼ばれる関数です。
~クラス名()という形で定義します。

主な用途は以下の通りです。

  • 動的に確保したメモリの解放
  • ファイルのクローズ
  • ネットワーク接続の切断
  • その他のクリーンアップ処理

これは本当に重要な概念で、特にメモリ管理が重要なC++では、適切なデストラクタの実装がメモリリークの防止につながります。

メンバ関数とメンバ変数の活用

リンドくん

リンドくん

クラスの中の関数と変数にはどんな使い方があるんですか?

たなべ

たなべ

メンバ変数はデータを保存する場所で、メンバ関数はそのデータを操作する機能なんだ。
適切に組み合わせることで、安全で使いやすいクラスが作れるよ!

ゲッターとセッター関数

private変数に安全にアクセスするため、**ゲッター(getter)セッター(setter)**という関数をよく使います。

class BankAccount
{
private:
    std::string accountNumber;
    std::string ownerName;
    double balance;

public:
    // コンストラクタ
    BankAccount(std::string accNum, std::string owner, double initialBalance) : accountNumber(accNum), ownerName(owner), balance(initialBalance)
    {
        std::cout << "口座が開設されました: " << ownerName << std::endl;
    }
    
    // ゲッター関数(値の取得)
    double getBalance() const
    {
        return balance;
    }
    
    std::string getOwnerName() const {
        return ownerName;
    }
    
    std::string getAccountNumber() const
    {
        return accountNumber;
    }
    
    // セッター関数(値の設定)- 安全性を考慮
    bool deposit(double amount)
    {
        if (amount > 0) {
            balance += amount;
            std::cout << amount << "円を預金しました。残高: " << balance << "円" << std::endl;
            return true;
        }
        std::cout << "無効な金額です" << std::endl;
        return false;
    }
    
    bool withdraw(double amount)
    {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            std::cout << amount << "円を引き出しました。残高: " << balance << "円" << std::endl;
            return true;
        }
        std::cout << "引き出しできません(残高不足または無効な金額)" << std::endl;
        return false;
    }
    
    // 口座情報の表示
    void showAccountInfo() const
    {
        std::cout << "=== 口座情報 ===" << std::endl;
        std::cout << "口座番号: " << accountNumber << std::endl;
        std::cout << "口座名義: " << ownerName << std::endl;
        std::cout << "残高: " << balance << "円" << std::endl;
        std::cout << "===============" << std::endl;
    }
};

const関数の重要性

上記のコードでconstが付いている関数は、そのオブジェクトの状態を変更しないことを保証しています。
これにより、コードの安全性と意図の明確化が図れます。

実際の使用例

int main()
{
    // 銀行口座オブジェクトの作成
    BankAccount myAccount("123-456-789", "田中太郎", 10000.0);
    
    // 口座情報の表示
    myAccount.showAccountInfo();
    
    // 預金と引き出し
    myAccount.deposit(5000);
    myAccount.withdraw(3000);
    myAccount.withdraw(15000);  // 残高不足でエラー
    
    // 最終的な残高確認
    std::cout << "最終残高: " << myAccount.getBalance() << "円" << std::endl;
    
    return 0;
}

この例では、カプセル化の利点がよく表れています。
balanceという重要なデータを直接変更できないようにし、適切な検証を経てからのみ変更できるようになっています。

まとめ

リンドくん

リンドくん

クラスって最初は難しそうに見えたけど、実は現実世界の考え方と同じなんですね!

たなべ

たなべ

その通り!クラスは現実世界を論理的に表現するためのツールなんだ。
基本をしっかり理解すれば、どんな複雑なシステムでも作れるようになるよ。今日学んだことを実際にコードに書いて試してみてね!

C++のクラスは、オブジェクト指向プログラミングの基礎となる重要な概念です。
この記事で学んだ内容をまとめると、以下のようになります。

クラスの基本概念

  • クラスは現実世界の「もの」をプログラムで表現するための設計図
  • データ(メンバ変数)と機能(メンバ関数)をひとまとめにできる
  • カプセル化により、データの安全性を保てる

実装のポイント

  • コンストラクタでオブジェクトの初期化を行う
  • デストラクタでリソースの適切な解放を行う
  • アクセス指定子(private/public/protected)を適切に使い分ける
  • ゲッター・セッター関数でデータへの安全なアクセスを提供する

クラスの概念を理解することで、より高度なプログラミング技術(継承、ポリモーフィズム、テンプレートなど)への道筋が見えてきます。
これは本当に重要な基礎知識なので、今回のサンプルコードを実際に動かしながら、理解を深めていってください。

関連するコンテンツ