C/C++のデータ構造

Cのstructと C++のclass

このレッスンでは、Cのstructと C++のclassで銀行口座を表し、2つのアプローチの違いを確認します。

はじめに

Cのstructは関連するデータをまとめるためのもので、それを処理する関数は外に別に書きます。C++のclassはデータとメンバー関数(member function)を1つの型にまとめ、privateでデータを隠してオブジェクトの正しさを守れます。

大切なポイント

  • 操作を書く前に、データ構造で常に成り立つべき条件(不変条件、invariant)を決めましょう。
  • 空のとき、満杯のとき、メモリ確保に失敗したときもきちんと処理しましょう。
  • 各操作の計算量とメモリ管理を、あわせて評価しましょう。

CとC++のコード例

C
実行 →
main.c
#include <stdio.h>

typedef struct {
    const char *owner;
    double balance;
} Account;

void deposit(Account *account, double amount) {
    if (amount > 0) {
        account->balance += amount;
    }
}

int main(void) {
    Account account = {"Ada", 100.0};
    deposit(&account, 25.0);
    printf("%s: %.2f\n", account.owner, account.balance);
    return 0;
}
実行結果
Ada: 125.00
C++
実行 →
main.cpp
#include <iomanip>
#include <iostream>
#include <string>
#include <utility>

class Account {
    std::string owner_;
    double balance_;

public:
    Account(std::string owner, double balance)
        : owner_(std::move(owner)), balance_(balance) {}

    void deposit(double amount) {
        if (amount > 0) {
            balance_ += amount;
        }
    }

    void print() const {
        std::cout << owner_ << ": " << std::fixed
                  << std::setprecision(2) << balance_ << '\n';
    }
};

int main() {
    Account account("Ada", 100.0);
    account.deposit(25.0);
    account.print();
}
実行結果
Ada: 125.00

CとC++の比較

Cのバージョンでは、関数depositはstruct Accountへのポインタを受け取り、金額が正のときだけ入金しますが、どのコードからでもbalanceを直接書き換えられてしまいます。C++のバージョンでは、balance_はpublic:より前に宣言されているので標準でprivateになり、deposit()のような正しいメソッドを通してしか変更できません。そのため、残高に勝手な値が入るといったおかしな状態を作るのはとても難しくなります。

C

データ(struct)と操作は分かれていて、メモリは手作業で確保・解放します。

C++

クラスとコンテナが、データの正しさを守り、リソースを自分で管理します。

発展問題

両方のバージョンを実行し、コードを書き換えて、それぞれの言語がもたらす安全性の違いを観察しましょう。

  • 空の状態と、容量の上限に達したときのテストを追加しましょう。
  • メモリを解放する処理を書き、メモリリークがないか確認しましょう。
  • 標準ライブラリの対応するコンテナと計算量を比べましょう。

チャレンジで練習する

このレッスンの内容を使って実際の問題を解きましょう。各チャレンジはブラウザで動き、すぐにテストされ、AI のレビューも受けられます。

すべてのチャレンジ