C++ のクラスの関数宣言における const キーワード
C++ では、const キーワードは、プログラムの実行中に変化せず一定のままである値を定義します。 これは、変数とそれらが保持するデータにとって非常に単純に思えます。
しかし、関数を定数として宣言するにはどうすればよいでしょうか。 この記事では、これらすべての質問とその他の多くの質問に答えますので、読み続けてください!
ここで見ている関数は、クラスに属する関数、つまりメンバー関数であることに注意してください。
C++ のクラスの関数宣言における const キーワード
実際の議論に入る前に、問題文を正しく理解しましょう。
クラスがデータ メンバーとメンバー関数を持つことができることは既にわかっています。 これらのメンバー関数は通常、次のようにクラス内で宣言されます -
class Demo // class
{
public:
int x;
int showDemo(); // member function declaration
}
ここで、メンバ関数 showDemo() はクラス Demo に属しています。 しかし、このようにメンバー関数の最後に const を追加すると、どういう意味になりますか?
class Demo // class
{
public:
int x;
int showDemo() const; // const member function declaration
}
これを理解するために、まずメンバー関数が C++ で通常どのように動作するかを見てみましょう。
C++ におけるメンバー関数の動作
以下のコードを見てください。 ここでは、まず、値が 2, に設定されたデータ メンバー chocolates とメンバー関数 Desert() を持つ Demo というクラスを定義します。
メンバー関数内で、welcome ステートメントと共に、データ メンバー chocolates の元の値を出力し、この値を 1 増やします。 さらに、次のステートメントで chocolates のインクリメントされた値を出力します。
最後に、オブジェクト demo を作成し、メンバー関数 Desert() を main ブロックから呼び出します。 また、オブジェクト demo および dot 演算子を使用して、chocolates の値を出力します。
#include <iostream>
using namespace std;
class Demo {
public:
int chocolates = 2;
public:
void Desert() // member function
{
cout << "Hello Chocolates" << endl;
cout << "Before change: " << chocolates << endl;
chocolates++;
cout << "After change: " << chocolates << endl;
}
};
int main() {
Demo demo;
demo.Desert();
cout << demo.chocolates << endl;
}
出力:
Hello Chocolates
Before change: 2
After change: 3
3
ここで、データ メンバー chocolates の値をメンバー関数内から変更したことがわかります。 変更をより正確に強調するために、この変数の値も main ブロックから出力しますが、それでも更新された値である 3 を取得します。
したがって、この例は、通常のメンバー関数を使用してオブジェクトを変更できることを意味します。
ここでは、1つのクラスと 1つのオブジェクトしかありません。 ただし、多くのクラスやオブジェクトを操作していると、誤ってオブジェクトに変更が加えられる可能性が高くなります。
これを回避するために、C++ の const メンバー関数を使用します。
C++ での const メンバー関数の動作
以下の例を見てください。 このコードは前のコードと同じですが、今回はメンバー関数の後に const キーワードを置きます。
#include <iostream>
using namespace std;
class Demo {
public:
int chocolates = 2;
public:
void Desert() const // const member function
{
cout << "Hello Chocolates" << endl;
cout << "Before change: " << chocolates << endl;
chocolates++;
cout << "After change: " << chocolates << endl;
}
};
int main() {
Demo demo;
demo.Desert();
cout << demo.chocolates << endl;
}
出力:
In member function 'void Demo::Desert() const':
error: increment of member 'Demo::chocolates' in read-only object
13 | chocolates++;
| ^~~~~~~~~~
メンバー関数を定数にすることで、出力がどのように変化するかを確認できます。 今度は、chocolates の値がインクリメントされず、エラーが発生します。
したがって、この例は、const メンバー関数を使用してオブジェクトを変更できないことを意味します。
上記の 2つのメンバー関数の比較により、そのような関数宣言での const の使用は、どのクラス メンバーにも変更がないことを意味すると結論付けます。 const をメソッドに追加すると、this ポインターは const オブジェクトを指すため、データ メンバーを変更することはできません。
以上のことを学んだ後、オブジェクトの変更を避けるために const メンバー関数を使用すると言えます。 しかし、次のように疑問に思うかもしれません: const オブジェクトの用途は何ですか?
オブジェクトを const として宣言すると、const メンバー関数のみを呼び出すことができます。 これは、const オブジェクトを変更できないためであり、この非変更は const メンバー関数によってのみ約束されます。
以下の例を見てください。
Desert クラス内には、desert() と desert() const という 2つのメンバー関数があります。 次に、main ブロック内で、2つのオブジェクト d と定数オブジェクト dd を定義します。
この例に関するいくつかの重要な点に注意してください。
- 同じ名前のメンバー関数が 2つあります。 唯一の違いは、そのうちの 1つが
constメンバー関数であることです。 - 2つのオブジェクトがあり、そのうちの 1つは定数オブジェクトです。
- どちらのオブジェクトがどの関数を呼び出すかを指定せずに、両方のオブジェクトからメンバ関数
desert()を呼び出します。
#include <iostream>
using namespace std;
class Desert {
private:
int cakes;
public:
void desert() { cout << "The first desert function." << endl; }
void desert() const // the const member function
{
cout << "The second const desert function." << endl;
}
};
int main() {
Desert d; // object of class
const Desert& dd = d; // constant object of class
d.desert();
dd.desert();
}
出力:
The first desert function.
The second const desert function.
出力は、通常のメンバー関数 desert() が通常のオブジェクト d によって自動的に呼び出されたことを明確に示しています。 一方、const メンバー関数 desert() const は、const オブジェクト dd によって自動的に呼び出されます。
オブジェクトから const キーワードを削除すると、出力が変わります。
int main() {
Desert d; // object of class
Desert& dd = d; // remove the constant keyword
d.desert();
dd.desert();
}
出力:
The first desert function.
The first desert function.
今回は両方のオブジェクトが最初の関数を呼び出します。 これは、const オブジェクトが const メンバー関数を呼び出すことを証明しています。
これは、非定数オブジェクトで const メンバー関数を呼び出せないという意味ではないことに注意してください。 ただし、非 const 関数は、非 const オブジェクトによってのみ呼び出すことができます。
ここまでは順調ですが、例外的なケースがあります。 mutable 量指定子をクラスのデータ メンバーと一緒に使用すると、const メソッドでもオブジェクトを変更できます。
この例を見てください。
#include <iostream>
using namespace std;
class Demo {
public:
mutable int chocolates = 2; // mutable quantifier
public:
void Desert() const // const member function
{
cout << "Hello Chocolates" << endl;
cout << "Before change: " << chocolates << endl;
chocolates++;
cout << "After change: " << chocolates << endl;
}
};
int main() {
Demo demo;
demo.Desert();
cout << demo.chocolates << endl;
}
出力:
Hello Chocolates
Before change: 2
After change: 3
3
今回は、データ メンバ chocolates で mutable 量指定子を使用しているため、以前のようなエラーは発生しません。 これは、メソッドが呼び出された回数を数えたい場合に役立ちます。
これはすべて、C++ の const メンバー関数に関するものです。 あちこちで少し変更を加えて、例を操作すればするほど、より明確になります。
C++ の const について詳しく知りたい場合は、この ドキュメントを参照してください。
まとめ
この記事では、C++ の const メンバー関数の概念について説明しました。 const メンバー関数は、オブジェクトが誤って変更されないことを保証し、const オブジェクトと const オブジェクト以外の両方で使用できます。
ただし、const オブジェクトは const メンバー関数のみを取り消すことができます。 また、データ メンバーで mutable キーワードを使用すると、const メンバー関数の通常の動作を変更でき、通常はカウントの目的で使用されます。