勉強記録　11日目 〜constなメンバ関数〜 - Effective C++ 第3版 -

#　はじめに

Effective C++ 第3版の3項10ページから勉強していきます。
今回は、「constなメンバ関数」についです。

# Effective C++ 第3版 - 3項　可能ならいつでもconstを使おう -

## constなメンバ関数

### メンバ関数にconst修飾子を付ける理由

メンバ関数にconst修飾子を付けるのは、
「その関数がconstなオブジェクトに対して使われる」or「メンバ関数を呼び出し、実行した時に、メンバ変数を変更する必要がない」ときに使われる。

そのような関数が重要である理由は2つあります。
1つ目は、「クラスのインタフェースを理解しやすくする」という理由です。
どのメンバ関数がオブジェクトを変更し、どのメンバ関数が変更しないか、という区別に容易につけられることは重要でしょう。
2つ目は、「constなオブジェクトに対して使える」という理由です。



次の、TextBlockというクラスについて考えてみます。

```cpp
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メンバ関数です。

```cpp
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を生成し、[]演算子を使用すると、下記のようになる。

```cpp
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なオブジェクトがよく使われる。

```cpp
 // 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[ ]が呼ばれる。

```cpp
class TextBlock {
 public:
 ...
 char& operator[](std::size_t position) const { return text_[position]; }; // コンパイルエラー
 ...
};
```

また、上のコードでエラーがでる理由は、
「関数内で値は変更していないが、参照返しのため、戻り値で代入できる。」
のためである。

以下に、勉強で使用したコードを示します。

### サンプルコード

```cpp:3_const_itr.cpp
#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);
}
```

### 実行結果

![3_const_mem.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367151/a38d659f-2b27-1cb9-59d5-ea65c463af57.png)



## 参考文献

・https://www.amazon.co.jp/gp/product/4621066099/ref=dbs_a_def_rwt_hsch_vapi_taft_p1_i0