#5 C++が自動で生成する関数(デフォルトコンストラクタ,コピーコンストラクタ,コピー代入演算子,デストラクタ)
今回の内容は Effective C++, 5項のまとめです.
クラスに何も記述しないような実装をしても,C++によって自動で生成される関数があります.それが,タイトルの通り,,,
です.
これは,Efective C++ の例そのままになってしまいますが,下記のようなクラス宣言をした場合,実際には下記のようなメソッドが生成されます.
下記のように何も実装しなくても....
class Empty() {};
実際には下記のようなメソッドが生成されている.
class Empty() { public: // Default Constructor Empty(){} // Default Destructor ~Empty(){} // Default Copy Constructor Empty(const Empty &rhs) { ... } // Default Copy Operator Empty& operator=(const Empty&rhs) { ... } };
1.コンストラクタ
デフォルトで生成されるコンストラクタは,下記のような特徴を持ってます.
サンプルコードを書いてみましたが,実際に Low のオブジェクトを生成(=デフォルトコンストラクタ)を呼んだタイミングで親クラスとデータメンバのコンストラクタが呼ばれていることがわかります.
#include <iostream> class Top { public: Top(); }; Top::Top() { std::cout << "Top Constructor!" << std::endl; } class Middle { public: Middle(); }; Middle::Middle() { std::cout << "Middle Constructor!" << std::endl; } class Low : Middle { private: Top top; }; int main(int, char**) { std::cout << "2nd Chapter 5" << std::endl; // Class Generation. Low low; }
2.デストラクタ
下記にサンプルコードを書きました.関数の仮想性が継承されることが下記のコードを実行すれば確認できますが,細かいことはまた別の項で.ここでは,親クラスのデストラクタが virtual で宣言されていれば,子クラスのデフォルトデストラクタも virtual になるということが確認できました.
下記のコードを実行してみると,インスタンスを delete するときに ChildChildBのデストラクタが呼ばれていないことがわかります.
#include <iostream> class ParentA { public: virtual ~ParentA(); }; ParentA::~ParentA() { std::cout << "ParentA Destructor!" << std::endl; } class ChildA : public ParentA { public: }; class ChildChildA : public ChildA { public: ~ChildChildA(); }; ChildChildA::~ChildChildA() { std::cout << "ChildChildA Destructor!" << std::endl; } class ParentB { public: ~ParentB(); }; ParentB::~ParentB() { std::cout << "ParentB Destructor!" << std::endl; } class ChildB : public ParentB { public: }; class ChildChildB : public ChildB { public: ~ChildChildB(); }; ChildChildB::~ChildChildB() { std::cout << "ChildChildB Destructor!" << std::endl; } int main(int, char**) { std::cout << "2nd Chapter 5" << std::endl; ChildA *A = new ChildChildA(); delete A; ChildB *B = new ChildChildB(); delete B; }
3.コピーコンストラクタ & コピー代入演算子
- コピー元の非staticデータメンバの単純なコピー.
コピーコンストラクタとコピー代入演算子が明示的に宣言されていない場合,こちらも直感的に一番しっくりくる関数が生成されます.振る舞いとしては,そのままコピーされるだけです.この”コピー”という表現がちょっと落とし穴だと思うんですが,これはまた後の項で出てくると思うので,そのうちに...
#include <iostream> #include <string> #include <vector> class A { public: int a; float b; std::string name; std::vector<int> vec; }; int main(int, char**) { std::cout << "Copy Constructor Example" << std::endl; { A a; a.a = 10; a.b = 10.0f; a.name = "sample"; a.vec.push_back(0); a.vec.push_back(1); a.vec.push_back(2); A aa(a); aa.vec.push_back(5); std::cout << "a.a : " << a.a << std::endl; std::cout << "a.b : " << a.b << std::endl; std::cout << "a.name : " << a.name << std::endl; for (std::size_t i = 0; i < a.vec.size(); i++) { std::cout << "elem : " << a.vec[i] << std::endl; } std::cout << std::endl; std::cout << "aa.a : " << aa.a << std::endl; std::cout << "aa.b : " << aa.b << std::endl; std::cout << "aa.name : " << aa.name << std::endl; for (std::size_t i = 0; i < aa.vec.size(); i++) { std::cout << "elem : " << aa.vec[i] << std::endl; } } std::cout << std::endl; std::cout << "Operator \"=\" Example" << std::endl; { A a; a.a = 10; a.b = 10.0f; a.name = "sample"; a.vec.push_back(0); a.vec.push_back(1); a.vec.push_back(2); A aa; aa = a; aa.vec.push_back(5); std::cout << "a.a : " << a.a << std::endl; std::cout << "a.b : " << a.b << std::endl; std::cout << "a.name : " << a.name << std::endl; for (std::size_t i = 0; i < a.vec.size(); i++) { std::cout << "elem : " << a.vec[i] << std::endl; } std::cout << std::endl; std::cout << "aa.a : " << aa.a << std::endl; std::cout << "aa.b : " << aa.b << std::endl; std::cout << "aa.name : " << aa.name << std::endl; for (std::size_t i = 0; i < aa.vec.size(); i++) { std::cout << "elem : " << aa.vec[i] << std::endl; } } return 0; }
参考文献
Effective C++ 第3版 (ADDISON-WESLEY PROFESSIONAL COMPUTI)
- 作者: スコットメイヤーズ,小林健一郎
- 出版社/メーカー: 丸善出版
- 発売日: 2014/03/18
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る