Menu
Coddy logo textTech

const メンバ関数

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 29/104。

前のレッスンで、ゲッターメソッドに const キーワードが付いているのを見たことがあるでしょう。しかし、関数のパラメータリストの後に const が現れるとき、それは正確には何を意味するのでしょうか?const メンバ関数は、オブジェクトのいかなるメンバ変数も変更しないことを約束するものです。

class Rectangle {
    int width;
    int height;
public:
    Rectangle(int w, int h) : width(w), height(h) {}
    
    int getArea() const {      // const メンバ関数
        return width * height;  // メンバの読み取りは許可されています
    }
    
    void setWidth(int w) {     // 非 const - オブジェクトを変更します
        width = w;
    }
};

パラメータリストの後の const は、コンパイラに「この関数はオブジェクトの状態を変更しません」と伝えます。const関数内でメンバ変数を変更しようとすると、コンパイラはエラーを生成します。

これは、constオブジェクトやconst参照を扱う際に不可欠になります。constオブジェクトは、constメンバ関数のみを呼び出すことができます:

void printArea(const Rectangle& rect) {
    std::cout << rect.getArea();    // OK - getArea() は const です
    // rect.setWidth(10);           // エラー - setWidth() は const ではありません
}

オブジェクトを変更しない関数を const としてマークすることは、良い習慣です。これにより、意図が明確になり、関数が const オブジェクトに対して動作できるようになり、コンパイラが誤った変更を検出するのに役立ちます。データを読み取るだけのメンバ関数はすべて、const としてマークする必要があります。

challenge icon

チャレンジ

簡単

const メンバ関数をいつ、なぜ使用するのかを実演する温度変換プログラムを作成しましょう。一部のメソッドはデータを読み取るだけで(const にすべき)、他のメソッドはオブジェクトの状態を変更するクラスを作成します。

コードを整理するために、2つのファイルを作成します。

  • Temperature.h: 温度値を保存し、それを読み取ったり変更したりするための様々な方法を提供する Temperature クラスを定義します。クラスには以下を含める必要があります。
    • 温度を保存するためのプライベートメンバ celsius (double)
    • 初期の摂氏値を受け取るコンストラクタ
    • 保存された値を返す getCelsius() メソッド — データを読み取るだけなので、これは const にする必要があります
    • celsius * 9.0 / 5.0 + 32.0 という式を使用して華氏の温度を計算して返す getFahrenheit() メソッド — 何も変更しないため、これも const です
    • celsius + 273.15 を使用してケルビン単位の温度を返す getKelvin() メソッド — これも const です
    • 保存された温度を更新する setCelsius(double value) メソッド — オブジェクトを変更するため、これは const にできません
    • 現在の温度にデルタを加算する adjustBy(double delta) メソッド — これも非 const です
  • main.cpp: 通常のオブジェクトと const オブジェクトの両方で const メンバ関数がどのように機能するかを実演します。入力から初期温度値を読み取り、以下の操作を行います。
    • 入力値を使用して Temperature オブジェクトを作成します
    • "Initial: <celsius>C = <fahrenheit>F = <kelvin>K" と出力します
    • 温度を 10.0 度調整します
    • "After adjustment: <celsius>C" と出力します
    • const 参照を受け取り、"Reading: <celsius>C, <fahrenheit>F" と出力するヘルパー関数 void printReadings(const Temperature& temp) を作成します。この関数は temp に対して const メソッドのみを呼び出すことができます。
    • 作成した温度オブジェクトを使用して printReadings() を呼び出します
    • 温度を 0.0 (氷点) に設定します
    • "Freezing point: <celsius>C = <fahrenheit>F" と出力します

すべての温度値は、<iomanip>std::fixedstd::setprecision(1) を使用して、小数点以下1桁でフォーマットしてください。

ここでの重要な洞察は、printReadings() が const 参照を受け取るため、const としてマークされたメソッドのみを呼び出すことができるということです。これが、ゲッターメソッドを適切に const としてマークすることが重要な理由です。これにより、オブジェクトを変更できないコンテキストでもそれらのメソッドを使用できるようになります。

チートシート

const メンバ関数は、オブジェクトのメンバ変数を一切変更しないことを保証します。const キーワードは、関数のパラメータリストの後に配置されます:

class Rectangle {
    int width;
    int height;
public:
    int getArea() const {      // const メンバ関数
        return width * height;  // メンバの読み取りは許可されています
    }
    
    void setWidth(int w) {     // 非 const - オブジェクトを変更します
        width = w;
    }
};

const メンバ関数はメンバ変数を読み取ることしかできず、変更することはできません。const 関数内でメンバ変数を変更しようとすると、コンパイラはエラーを生成します。

const オブジェクトまたは const 参照は、const メンバ関数のみを呼び出すことができます:

void printArea(const Rectangle& rect) {
    std::cout << rect.getArea();    // OK - getArea() は const です
    // rect.setWidth(10);           // エラー - setWidth() は const ではありません
}

オブジェクトを変更しない関数は const としてマークしてください。これにより、意図が明確になり、関数が const オブジェクトで動作できるようになり、コンパイラが誤った変更を検出するのに役立ちます。データを読み取るだけのメンバ関数は、すべて const としてマークする必要があります。

自分で試してみよう

#include <iostream>
#include <iomanip>
#include "Temperature.h"
using namespace std;

// TODO: ここで Temperature クラスのメソッドを実装する
// コンストラクタ
Temperature::Temperature(double initialCelsius) {
    // TODO: celsius を初期化する
}

// TODO: getCelsius() を const として実装する

// TODO: getFahrenheit() を const として実装する

// TODO: getKelvin() を const として実装する

// TODO: setCelsius(double value) を実装する

// TODO: adjustBy(double delta) を実装する


// TODO: const Temperature& を受け取るヘルパー関数 printReadings を作成する 
// そして "Reading: <celsius>C, <fahrenheit>F" を出力する
// Note: この関数は temp に対して const メソッドのみを呼び出すことができます!


int main() {
    double initialTemp;
    cin >> initialTemp;

    // 出力フォーマットを設定する
    cout << fixed << setprecision(1);

    // TODO: 入力値を使用して Temperature オブジェクトを作成する

    // TODO: "Initial: <celsius>C = <fahrenheit>F = <kelvin>K" を出力する

    // TODO: 温度を 10.0 度調整する

    // TODO: "After adjustment: <celsius>C" を出力する

    // TODO: 作成した温度オブジェクトを使用して printReadings() を呼び出す

    // TODO: 温度を 0.0 (氷点) に設定する

    // TODO: "Freezing point: <celsius>C = <fahrenheit>F" を出力する

    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン