mutableキーワード
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 30/104。
constメンバー関数は、どのメンバー変数も変更できないことを学びました。しかし、キャッシュやアクセスカウンターのように、オブジェクトの論理状態に影響を与えないメンバーを変更する必要がある場合もあります。mutableキーワードは、constの制限に対する例外を提供します。
mutableメンバーは、constメンバー関数の内部でも変更できます。
class DataFetcher {
std::string data;
mutable int accessCount; // const関数内で変更可能
public:
DataFetcher(std::string d) : data(d), accessCount(0) {}
std::string getData() const {
accessCount++; // accessCountがmutableであるため許可される
return data;
}
int getAccessCount() const {
return accessCount;
}
};mutable がなければ、const 関数 getData() 内で accessCount をインクリメントすると、コンパイラーエラーが発生します。mutable キーワードはコンパイラーに次のように伝えます。「オブジェクトが const と見なされている場合でも、このメンバーは変更できます。」
mutableの一般的なユースケースには、計算値のキャッシュ、アクセス統計の追跡、ミューテックスなどの同期プリミティブが含まれます。いずれの場合も、変更によってオブジェクトが論理的に表すものが変わることはありません。キャッシュされた値は単なる最適化であり、アクセスカウンターは使用状況に関するメタデータだからです。
class Circle {
double radius;
mutable double cachedArea;
mutable bool areaCached;
public:
double getArea() const {
if (!areaCached) {
cachedArea = 3.14159 * radius * radius;
areaCached = true;
}
return cachedArea;
}
};mutableは控えめに使用してください。オブジェクトの観測可能な状態に影響を与えないメンバーにのみ適用すべきです。
チャレンジ
簡単読み取り専用メソッドの const-correctness を損なうことなく、ドキュメントが何回閲覧されたかを追跡するドキュメントビューアーを作成しましょう。これは mutable キーワードの最適な使用例です。
コードを整理するために、2つのファイルを作成します。
Document.h: コンテンツを格納し、閲覧統計を密かに追跡するDocumentクラスを定義します。クラスには次の要素が必要です。- private メンバー:
title(string)、content(string)、および const メソッド内でも変更できるmutable int viewCount - title と content を受け取り、
viewCountを0に初期化するコンストラクター - title を返す
getTitle()const メソッド viewCountをインクリメントし(mutable なので許可されます)、content を返すgetContent()const メソッド- content が何回アクセスされたかを返す
getViewCount()const メソッド - content の最初の20文字に続けて
"..."を返すgetPreview()const メソッド。これは単なるプレビューなので、view count をインクリメントしてはいけません
- private メンバー:
main.cpp: メソッドを論理的に const のままにしつつ、mutable によってアクセス統計を追跡できることを示します。ドキュメントの title と content を input から読み込み(それぞれ別の行)、次の処理を行います。- input の値で
Documentを作成する "Title: <title>"を出力する"Preview: <preview>"を出力する"Views after preview: <count>"を出力するgetContent()を使用して full content にアクセスし、"Content: <content>"を出力する"Views after first read: <count>"を出力する- content に再度アクセスし、
"Content: <content>"を出力する "Views after second read: <count>"を出力する"[Const Access] <title>: <content>"を出力するヘルパー関数void displayDocument(const Document& doc)を作成する。これにより、const メソッドが const 参照で動作することを確認できます- document を引数として
displayDocument()を呼び出す "Final view count: <count>"を出力する
- input の値で
プレビュー後の view count は 0 になるはずです(プレビューは full view としてカウントされないため)。その後、getContent() が呼び出されるたびにインクリメントされます。mutable キーワードによってこれが可能になります。mutable がなければ、getContent() のような const メソッド内で viewCount を変更することはできません。
content が20文字より短い場合、getPreview() は content 全体に続けて "..." を返す必要があります。
自分で試してみよう
#include <iostream>
#include <string>
#include "Document.h"
using namespace std;
// TODO: displayDocument 関数を実装する
// 次のように出力する必要があります: "[Const Access] <title>: <content>"
void displayDocument(const Document& doc) {
// TODO: この関数を実装する
}
int main() {
// 入力を読み取る
string title;
string content;
getline(cin, title);
getline(cin, content);
// TODO: 入力値で Document を作成する
// TODO: "Title: <title>" を出力する
// TODO: "Preview: <preview>" を出力する
// TODO: Print "Views after preview: <count>"
// TODO: フルコンテンツにアクセスして "Content: <content>" を出力する
// TODO: Print "Views after first read: <count>"
// TODO: 再度コンテンツにアクセスして "Content: <content>" を出力する
// TODO: Print "Views after second read: <count>"
// TODO: ドキュメントで displayDocument() を呼び出す
// TODO: Print "Final view count: <count>"
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタパラメータ付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ徹底解説Rule of Three / Five / Zero復習 - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () の演算子オーバーロード型変換演算子復習 - Matrixクラス自分で練習してみよう: C++オンラインコンパイラ