はじめに
Effective C++ 第3版の3項15ページから勉強していきます。
今回は、「constなメンバ関数と非constなメンバ関数の重複を取り除く」についです。
Effective C++ 第3版 - 3項 可能ならいつでもconstを使おう -
constなメンバ関数と非constなメンバ関数の重複を取り除く
非constなメンバ関数内でconstなメンバ関数を呼び出すには?
前々回の投稿で、メンバ関数にconst修飾子を付けると、メンバ関数内でメンバ変数を変更していけないということを勉強しました。
class ClassA {
public:
explicit ClassA(int x, int y) : x_(x), y_(y){};
const int& testFunc() const;
int& testFunc();
int getX() { return x_; };
int getY() { return y_; };
private:
int x_;
int y_;
};
const int& ClassA::testFunc() const {
// x_ = 5; // コンパイルエラー // メンバ変数を変更しようとしているため
std::cout << "const testFunc() " << std::endl;
return x_;
}
非constなメンバ関数内でconstなメンバ関数を呼び出す必要性は?
2つの関数内が同じ処理をする場合にコードの重複を行わないため。
下のコードがその例です。
class ClassA {
public:
explicit ClassA(int x, int y) : x_(x), y_(y){};
const int& testFunc() const;
int& testFunc();
int getX() { return x_; };
int getY() { return y_; };
private:
int x_;
int y_;
};
// コードの重複
int& ClassA::testFunc() {
std::cout << "hello" << std::endl; // 重複
return x_; // 重複
}
const int& ClassA::testFunc() const {
std::cout << "hello" << std::endl; // 重複
return x_; // 重複
}
この重複を解決するために、非constなメンバ関数内でconstなメンバ関数を呼び出します。
class ClassA {
public:
explicit ClassA(int x, int y) : x_(x), y_(y){};
const int& testFunc() const;
int& testFunc();
int getX() { return x_; };
int getY() { return y_; };
private:
int x_;
int y_;
};
int& ClassA::testFunc() {
std::cout << "testFunc() " << std::endl; // 分かりやすくするためのprint
return const_cast<int&>(static_cast<const ClassA&>(*this).testFunc());
}
const int& ClassA::testFunc() const {
std::cout << "const testFunc() " << std::endl;
return x_;
}
上のコードは、非constなメンバ関数内でconstなメンバ関数を呼び出しています。
もし、非constな関数(TestFunc())内で、単にTestFunc()を呼びだせば、再帰的にこの関数(TestFunc())を呼び出すことになります。(無限再帰)
これを避けるために、明示的にconstなTestFunc()を呼び出すようにする必要があります。
そこで、まず、*thisを、ClassA& から const ClassA&にキャストします。
これは、constなメンバ関数TestFunc()を呼び出すためのキャストです。
そして、constなメンバ関数TestFunc()の戻り値のconst int& を int& にキャストします。
これは、非constなメンバ関数の戻り値の型に合わせるためのキャストです。
このような処理を書くことで重複がなくなりました。
しかし、重複を消すことはできたがすごく分かりにくいコードとなりました。
Effective C++にも、「見栄えの悪いコードを使うかどうかを決めるのは、プログラマであるあなた」と書いていたので、
私は、使わずに、非constなメンバ関数からconstなメンバ関数を呼べるという知識を持っているだけにしようと思います。
以下に、勉強で使用したコードを示します。
サンプルコード
# include <iostream>
class ClassA {
public:
explicit ClassA(int x, int y) : x_(x), y_(y){};
const int& testFunc() const;
int& testFunc();
int getX() { return x_; };
int getY() { return y_; };
private:
int x_;
int y_;
};
int& ClassA::testFunc() {
std::cout << "testFunc() " << std::endl; // 分かりやすくするためのprint
return const_cast<int&>(static_cast<const ClassA&>(*this).testFunc());
}
const int& ClassA::testFunc() const {
std::cout << "const testFunc() " << std::endl;
return x_;
}
int main(int argc, char* argv[]) {
std::cout << "3_const_call.cpp" << std::endl;
ClassA c1(1, 2);
c1.testFunc();
const ClassA cc1(2, 3);
cc1.testFunc();
}
実行結果
実行結果から分かるように、
c1.testFunc();
は、非constなtestFunc()が呼ばれた後に、constなtestFunc()が呼ばれていることが分かる。
参考文献
・https://www.amazon.co.jp/gp/product/4621066099/ref=dbs_a_def_rwt_hsch_vapi_taft_p1_i0