🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

4886閲覧

C++ の委譲コンストラクタが必要になった理由

pqwm

総合スコア29

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

2クリップ

投稿2021/02/12 11:24

編集2021/02/12 11:27

下のリファレンス内にある C++ における委譲コンストラクタの「この機能が必要になった背景・経緯」でいくつか分からないことがあったので質問しました.
委譲コンストラクタ cpprefjp - C++日本語リファレンス

委譲コンストラクタがなかった頃、複数のコンストラクタで共通の処理を行うために、コンストラクタの本体(body)で共通処理の関数を呼び出していた。しかしこれは、コンストラクタでの初期化が完了したあとに行われる共通処理であるために、パフォーマンスを阻害していた。パフォーマンスを維持するためには、コンストラクタごとに同じ処理を書く必要があり、コードの肥大化が問題となった。

質問の背景

次の質問のある解答にコメントしていますが,提案もあり改めて質問を作成しました.いくつか調べて分かった箇所もあるので正しい理解か確認したいです.
構造体のメンバ関数のようなもので構造体と同じ名前がついているもの

参考サイトは次の通りです.最後のものは理解できてないのですが初心者が犯しやすい間違いで参考にしています.
コンストラクタとメンバ初期化子リスト cppreference.com
メンバ初期化子リスト MaryCore
N1986 Delegating Constructors (revision 3)

具体的な質問内容

**1.**コンストラクタの本体とはどの部分で,どのようなコードでしょうか?

コンストラクタの本体(body)で共通処理の関数を呼び出していた。

理解
コンストラクタの本体は初期化子リストも含めた部分.共通処理の関数は CommonInit()

C++

1class X {...} 2 3class Y { 4 X a, b, c, d; 5public: 6 Y() { CommonInit(); } //「{ CommonInit(); }」がコンストラクタの本体 7 Y(X a_, X b_) : a(a_), b(b_) { CommonInit(); } //「a(a_), b(b_) { CommonInit(); }」がコンストラクタの本体 8 9 // 共通処理の関数 10 void CommonInit() { c = X(...); d = X(...); } 11};

 

2. 「パフォーマンスを阻害していた」とはどの部分ですか?

理解
初期化の順序としてコンストラクタの本体は非静的データメンバの初期化後に呼ばれるので,CommonInit() 内で非静的データメンバの初期化を行うと各データメンバのデフォルトコンストラクタが呼ばれた後に代入などの処理が行われ,デフォルトコンストラクタの呼び出しが冗長となりパフォーマンスを阻害していた.

 
**3.**下の箇所のコンストラクタごとに同じ処理を書くとはどのようなものですか?

パフォーマンスを維持するためには,コンストラクタごとに同じ処理を書く必要があり,コードの肥大化が問題となった

理解
メンバ初期化子リストでデータメンバの初期化を書くやり方はデフォルトコンストラクタを呼び出すのでパフォーマンスを維持する.ただ,これは初期化子リストのことで違う気がする.コンストラクタのブロック文に CommonInit() をコピペする方法はコードの肥大化には当てはまるがパフォーマンスを維持するかは分からない.

C++

1class X {...} 2 3class Y { 4X a, b, c, d; 5public: 6 Y() : c(X(...)), d(X(...)) {} 7 Y(X a_, X b_) : a(a_), b(b_), c(X(...)), d(X(...)) {} 8};

 

4. 初心者が犯しやすい間違い次の例で i = 100 ではなく i = -1 が出力されるのは何故ですか?

C++

1class X { 2int i = -1; 3public: 4 X() { std::cout << "i = " << i << std::endl; } 5 X(int i_) : i(i_) { X(); } 6} 7 8int main() { 9 X x(100); 10 return 0; 11}

理解
分かりませんでした.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

私も詳しいわけではないのですが、提案してしまった手前、私なりの解釈は出しておきます。
間違っていたら補足をお願いします。

1.コンストラクタの本体とはどの部分で,どのようなコードでしょうか?

本体は初期化子リストを除いた{}の内側。
(初期化子で共通処理の関数を呼び出しても、2.で説明するパフォーマンス阻害が発生しない)

C++

1class Test1 { 2 std::vector<int> v; 3 void init(int n) { v = { n, 2, 3 }; } 4public: 5 Test1() { init(1); } 6 Test1(int n) { init(n); } 7};

2. 「パフォーマンスを阻害していた」とはどの部分ですか?

コンストラクタ本体が実行される前にvvectorのデフォルトコンストラクタで作成され、
さらにinit内で別の値を作成し、その値で上書きされるので、二度手間となる。

3.下の箇所のコンストラクタごとに同じ処理を書くとはどのようなものですか?

同じような初期化子を書くという意味。

C++

1class Test2 { 2 std::vector<int> v; 3public: 4 Test2() : v{ 1, 2, 3 } {} 5 Test2(int n) : v{ n, 2, 3 } {} 6};

4. 初心者が犯しやすい間違い次の例でi = 100ではなくi = -1が出力されるのは何故ですか?

コンストラクタ本体でX();と書いた場合、現在コンストラクタで作成しているオブジェクトとは別に
新たなX型の一時オブジェクトを作成し、デフォルトコンストラクタを呼び出し、
その直後に一時オブジェクトを開放するという動きになる。

デフォルトコンストラクタで作成された一時オブジェクトのiは-1なので-1が出力される。


追記(1.のカッコ内の説明)

初期化が個別のメンバ変数ごとに独立していれば、初期化子で共通処理関数を呼び出せる。
この場合は、デフォルトコンストラクタが呼ばれないため、
2.で説明するパフォーマンス阻害は発生しない。

C++

1class Test3 { 2 std::vector<int> v; 3 static std::vector<int> calc(int n) { return { n, 2, 3 }; } 4public: 5 Test3() : v(calc(1)) {} 6 Test3(int n) : v(calc(n)) {} 7};

投稿2021/02/12 13:47

編集2021/02/12 21:25
actorbug

総合スコア2420

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

pqwm

2021/02/12 15:29

回答有難うございます.自分なりに理解できたと思います,有難うございます. 1. コンストラクタの本体は参考サイトの一番上 https://ja.cppreference.com/w/cpp/language/initializer_list を参考にしました.ただ,初期化子リストもコンストラクタの本体に入れると「初期化の順序」で書かれている,  3) その後、非静的データメンバがクラス定義内の宣言順で初期化されます。  4) 最後に、コンストラクタの本体が実行されます。 が実際の挙動と辻褄が合わないです.初期化子リストを除いた部分という理解の方がいろいろ辻褄が合いそうです. > (初期化子で共通処理の関数を呼び出しても、2.で説明するパフォーマンス阻害が発生しない) 初期化子でメンバ関数 init() を呼び出すことはできるのでしょうか. 2. , 3. は自分と同じ理解だと思います. 4. はその通りだと思います.「this->X()」だと勘違いしていました. 余談ですが次のサイトで VC なら「this->X::X()」と呼び出せるとあり驚きました(i = -1 を出力するので間違ってそうですが). https://stackoverflow.com/questions/308276/can-i-call-a-constructor-from-another-constructor-do-constructor-chaining-in-c
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問