はじめに
Effective C++ 第3版の3項10ページから勉強していきます。
今回は、「constなメンバ関数」についです。
Effective C++ 第3版 - 3項 可能ならいつでもconstを使おう -
constなメンバ関数
メンバ関数にconst修飾子を付ける理由
メンバ関数にconst修飾子を付けるのは、
「その関数がconstなオブジェクトに対して使われる」or「メンバ関数を呼び出し、実行した時に、メンバ変数を変更する必要がない」ときに使われる。
そのような関数が重要である理由は2つあります。
1つ目は、「クラスのインタフェースを理解しやすくする」という理由です。
どのメンバ関数がオブジェクトを変更し、どのメンバ関数が変更しないか、という区別に容易につけられることは重要でしょう。
2つ目は、「constなオブジェクトに対して使える」という理由です。
次の、TextBlockというクラスについて考えてみます。
class TextBlock {
public:
explicit TextBlock(std::string text) : text_(text){};
const char& operator[](std::size_t position) const { return text_[position]; };
char& operator[](std::size_t position) { return text_[position]; };
// char& operator[](std::size_t position) const { return text_[position]; }; // コンパイルエラー
void testFunc() const { std::cout << "text : " << text_ << std::endl; };
// void testFunc() { std::cout << __func__ << " : 2" << std::endl; }
private:
std::string text_;
};
メンバ関数の右側にconstをつけると、そのメンバ関数内ではメンバ変数の変更ができなくなります。
このメンバ関数をconstメンバ関数と呼んだりもします。
下の2つが、TextBlockのconstメンバ関数です。
class TextBlock{
public:
void testFunc() const { std::cout << "text : " << text_ << std::endl; };
const char& operator[](std::size_t position) const { return text_[position]; };
...
};
また、オーバーロードで、constの有る無しだけが異なる2つのバージョンの[]演算子を書くと、
constでないTexttBlockに対しても、constなTextBlockに対しても、[]演算子を使うことができる。
TextBlockとconstなTextBlockを生成し、[]演算子を使用すると、下記のようになる。
TextBlock tb("Hello");
std::cout << tb[0] << std::endl; // constでないTextBlock::operator[]を呼び出す
tb[0] = 'a'; // 代入可能
const TextBlock ctb("World");
std::cout << ctb[0] << std::endl; // constありのTextBlock::operator[]を呼び出す
// ctb[0] = 'a'; // コンパイルエラー // 代入不可
const付きのTextBlockはconst付きのoperator[ ]を呼び出している。
constなしのTextBlockの[ ]演算子の返り値は参照のため、読み込み・書き込みが可能である。
constありのTextBlockの[ ]演算子の返り値はconstが付いているため、読み込みのみ可能である。
また、関数の引数のポインタ渡しや参照渡しに、constなオブジェクトがよく使われる。
// const char& operator[](std::size_t position) const が呼ばれる
void print(const TextBlock& ctb) { std::cout << ctb[0] << std::endl; }
print(ctb);
print(tb);
上のような場合は、print関数内でconst付きのoperator[ ]が呼ばれる。
class TextBlock {
public:
...
char& operator[](std::size_t position) const { return text_[position]; }; // コンパイルエラー
...
};
また、上のコードでエラーがでる理由は、
「関数内で値は変更していないが、参照返しのため、戻り値で代入できる。」
のためである。
以下に、勉強で使用したコードを示します。
サンプルコード
# include <iostream>
# include <vector>
class TextBlock {
public:
explicit TextBlock(std::string text) : text_(text){};
const char& operator[](std::size_t position) const { return text_[position]; };
char& operator[](std::size_t position) { return text_[position]; };
// char& operator[](std::size_t position) const { return text_[position]; }; // エラーがでる理由
void testFunc() const { std::cout << "text : " << text_ << std::endl; };
// void testFunc() { std::cout << __func__ << " : 2" << std::endl; }
private:
std::string text_;
};
void print(const TextBlock& ctb) { std::cout << ctb[0] << std::endl; } // const char& operator[](std::size_t position) const が呼ばれる
int main(int argc, char* argv[]) {
std::cout << "3_const_mem.cpp" << std::endl;
TextBlock tb("Hello");
std::cout << tb[0] << std::endl; // constでないTextBlock::operator[]を呼び出す
tb[0] = 'a';
std::cout << tb[0] << std::endl;
const TextBlock ctb("World");
std::cout << ctb[0] << std::endl; // constなTextBlock::operator[]を呼び出す
tb.testFunc();
ctb.testFunc();
print(ctb);
print(tb);
}
実行結果
参考文献
・https://www.amazon.co.jp/gp/product/4621066099/ref=dbs_a_def_rwt_hsch_vapi_taft_p1_i0