# はじめに
Effective C++ 第3版の5項27ページから勉強していきます。
今回は、「コンパイラが自動で生成する関数」についです。
Effective C++ 第3版 - 5項 C++が自動で書き、自動で呼び出す関数を知ろう -
コンパイラが自動で生成する関数
クラスにおけるコンパイラが自動で生成する関数
クラス内で、コピーコンストラクタ、コピー代入演算子、デストラクタを宣言しなけらば、コンパイラが自動でそれらを生成することになっています。
また、コンストラクタを1つも宣言しなければ、コンパイラは自動でデフォルトコンストラクタを生成することになっています。
つまり、
class ClassA{};
と書いても、それは、本質的には、次のように書いたものと同じになります。
class ClassA {
public:
ClassA(){}; // デフォルトコンストラクタ
ClassA(const ClassA& rhs){}; // コピーコンストラクタ
~ClassA(){}; // デストラクタ
ClassA& operator=(const ClassA& rhs){}; // コピー代入演算子
};
ただし、上で書いたような関数は、必要なときにだけ生成されることになっています。
生成が必要なのは、以下のようなコードを書いた時です。
class ClassA {};
ClassA ca1; // デフォルトコンストラクタとデストラクタを生成が必要
ClassA ca2(ca1); // コピーコンストラクタの生成が必要
ca2 = ca1; // コピー代入演算子の生成が必要
コンパイラが生成してくれる関数はどのように機能するのか?
コンパイラが生成してくれるデフォルトコンストラクタとデストラクタは、それぞれ、基底クラスや非staticなデータメンバのコンストラクタとデストラクタを(見えないところで)で呼び出してくれます。
また、関数の仮想性は継承されるため、基底クラスが仮想デストラクタを持てば、派生クラスにコンパイラが生成するデストラクタも仮想となります。
しかし、基底クラスのデストラクタが仮想でない場合、コンストラクタが生成するデストラクタは、仮想にはなりません。(文献[1]より引用)
これは、どういう意味だろう?
コンストラクタが生成するデストラクタとは??
コンパイラが自動で生成するコピーコンストラクタとコピー代入演算子は、コピー元の非staticなデータメンバを、コピー先のオブジェクトに、単純にコピーするだけです。
以下のようなクラスで例を説明します
template<typename T>
class ClassB {
public:
explicit ClassB(const std::string& name, const T& value) {
name_ = name;
value_ = value;
};
private:
std::string name_;
T value_;
};
ClassBは、コンストラクタが明示的に宣言されているので、コンパイラはデフォルトコンストラクタを生成しません。
これは、重要です。
つまり、「引数を取るコンストラクタ」を持つようクラスを設計した場合、コンパイラが安易に「引数を取らないコンストラクタ」を生成し、設計をだいなしにしてしまう、などということはない。
ClassB<int> cb1; // NG
ClassBは、コピーコンストラクタもコピー代入演算子も宣言されていません。
そのため、(必要な場合は)コンパイラが生成します。
以下のように、コピーコンストラクタを呼び出してみます。
ClassB<int> cb1("First", 1);
ClassB<int> cb2(cb1); // コピーコンストラクタの呼び出し
コンパイラが生成するコピーコンストラクタによって、cb2.name_ と cb2.value_ が初期化されます。
その際、それぞれ cb1.name_ と cb1.value_ の値が使われます。
以下に、勉強で使用したコードを示します。
サンプルコード
#include <iostream>
class ClassA {};
/*
class ClassA {
public:
ClassA(){}; // デフォルトコンストラクタ
ClassA(const ClassA& rhs){}; // コピーコンストラクタ
~ClassA(){}; // デストラクタ
ClassA& operator=(const ClassA& rhs){}; // コピー代入演算子
};*/
template <typename T>
class ClassB {
public:
explicit ClassB(const std::string& name, const T& value) {
name_ = name;
value_ = value;
};
std::string getName() { return name_; };
T getValue() { return value_; };
private:
std::string name_;
T value_;
};
int main(int argc, char* argv[]) {
std::cout << "5_class.cpp" << std::endl;
ClassA ca1; // デフォルトコンストラクタとデストラクタを生成が必要
ClassA ca2(ca1); // コピーコンストラクタの生成が必要
ca2 = ca1; // コピー代入演算子の生成が必要
// ClassB<int> cb1;
ClassB<int> cb1("First", 1);
ClassB<int> cb2(cb1); // コピーコンストラクタの呼び出し
std::cout << "##### cb1 ##### " << std::endl;
std::cout << "name : " << cb1.getName() << " , value : " << cb1.getValue() << std::endl;
std::cout << "##### cb2 ##### " << std::endl;
std::cout << "name : " << cb2.getName() << " , value : " << cb2.getValue() << std::endl;
}
実行結果
参考文献
[1] https://www.amazon.co.jp/gp/product/4621066099/ref=dbs_a_def_rwt_hsch_vapi_taft_p1_i0