質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.35%
コンストラクタ

オブジェクト指向言語において、オブジェクトを生成時に呼び出され、データの初期化などを行なう関数・メソッドのことである。

C++

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

Q&A

解決済

4回答

1966閲覧

基底クラスのコンストラクタの複数回の呼び出し

tails

総合スコア22

コンストラクタ

オブジェクト指向言語において、オブジェクトを生成時に呼び出され、データの初期化などを行なう関数・メソッドのことである。

C++

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

0グッド

0クリップ

投稿2021/10/08 19:28

編集2021/10/09 04:31

質問

基底クラスのコンストラクタの複数回の呼び出しは、未定義の動作ですか?
それとも、何らかの定義された動作になりますか?

コード

C++

1struct base { 2 base(std::string str) : m_str(str) {} 3 4private: 5 std::string m_str; 6}; 7 8struct child: base { 9 child() : base("first") { 10 // base は既に初期化済みだが、再び呼び出す 11 // 修正前: base("second"); 12 // 修正後 13 this->base::base("second"); 14 } 15};

これをやりたい理由

child のコンストラクタ内で複雑な文字列を作成して、それを base に渡す必要があるため、一旦 base を空文字列で初期化した後、複雑な文字列を作成して、その後再度 base 部分をその文字列で初期化し直したいと考えています。

追記

申し訳ありません。
コードが一部、質問したい内容と異なっていました。
修正後のコードでご回答願いたいです。よろしくお願いします。

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

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

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

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

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

guest

回答4

0

ベストアンサー

そのコードだと、おそらく質問者さんの意図通りには動いていません。
base("second");は、base型の一時オブジェクトを作成して、そのまま捨てるという意味になります。

複雑な文字列を生成する処理を別関数にするのではだめなのでしょうか。

c++

1struct child: base { 2 static std::string aaa() { 3 // 複雑な文字列を生成する処理 4 } 5 child() : base(aaa()) { 6 } 7};

投稿2021/10/08 20:31

actorbug

総合スコア2429

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

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

tails

2021/10/09 04:59 編集

ご回答ありがとうございます。 this->base::base("second") と書きたかったのですが、誤って base("second") としてしまいました。申し訳ありません。 確かにそのように別関数にすることでも実現できますね。 実際の所、それで良いのですが、ところで上記修正後のコードのような基底クラスの複数回の呼び出しが、未定義動作かどうかが気になっていますので、そちらについてご回答いただけるとありがたいです。 よろしくお願いします。
actorbug

2021/10/09 04:54

そんなことをしたら、m_strでメモリを確保していたとしても解放されないのではないでしょうか。 少なくとも、事前に手動でbaseのデストラクタを呼ぶ必要はあるように思います。 this->base::~base(); デストラクタを読んでからコンストラクタを呼ぶのが合法かについては私は知りません。 だれか詳しい方に代わりに解説をお願いします。
tails

2021/10/09 05:06

なるほど、、仰る通りですね。 少なくとも、事前に手動でbaseのデストラクタの呼び出しは必要そうですね。 ありがとうございます。
actorbug

2021/10/09 05:43

this->base::base("second"); この記法が許されるのは、Visual C++ぐらいのようです。 g++、clang++ともにエラーになります。
tails

2021/10/09 06:34

それは知らなかったです。勉強になりました。 そもそも標準のC++で許された処理でないのなら、未定義動作かどうかを聞く意味もなくなってしまいましたね。 そもそも移植性がないということなので…。 ありがとうございました。
guest

0

やったら未定義かというよりは出来ないようになっていると言えるでしょう。 コンストラクタというのは便宜的にクラス自身と同じ名前であるかのように記述しますが、言語仕様上の理屈ではコンストラクタに名前はないということになっていてコンストラクタを呼出せる文脈以外でただのメンバ関数のようにコンストラクタを呼出すことは出来ません。

cpp

1struct foo { 2 foo(){} 3}; 4 5int main() { 6 foo bar; 7 // foo というメンバは存在しない! 8 bar.foo(); 9}

投稿2021/10/09 05:32

SaitoAtsushi

総合スコア5684

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

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

tails

2021/10/09 06:37

普通はコンパイルエラーになる、という事実を知りませんでした。 未定義動作かどうか以前に、コンパイルエラーのようですので、actorbug さんの回答で解決しました。 ありがとうございました。
guest

0

これ↓でもよさげ:

C++

1struct base { 2 base(std::string str) : m_str(str) {} 3 4protected: // 追加 5 void set_str(const std::string& str) { m_str = str; } 6 7private: 8 std::string m_str; 9}; 10 11struct child: base { 12 child() : base("") { set_str("second"); } 13};

投稿2021/10/09 00:15

episteme

総合スコア16612

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

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

tails

2021/10/09 04:57

ご回答ありがとうございます。 そのような方法で実現はできそうですね。 実際の所、それで良いのですが、ところで最初の質問の、修正後のコードのような基底クラスの複数回の呼び出しが、未定義動作かどうかが気になっていますので、そちらについてご回答いただけるとありがたいです。 よろしくお願いします。
episteme

2021/10/09 06:16

「コンストラクタとデストラクタは対になって呼び出されるべき」を守るのがスジなので、僕はたとえできてもやらない。言語としてもコンストラクタを何度も呼ぶことを想定してはいないと思う。 # 未定義動作か否かについては"わかんない"ですごめんなさい。
tails

2021/10/09 06:39 編集

分かりました。ありがとうございました。(そもそもコンパイルエラーでした)
guest

0

こんな感じでどうでしょうか?

①placement new

c++

1struct child: base { 2 child() : base("first") {3new ( ( base* )this ) base("second"); // (base*)がないと多重継承時に失敗する 4} 5};

②ラムダ式

c++

1struct child: base { 2child() : base([](){ /*複雑な文字列を生成する処理*/ }()) { 3} 4};

投稿2021/10/08 23:05

編集2021/10/08 23:07
Serbonis

総合スコア586

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

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

tails

2021/10/09 04:59 編集

① placement new で無理やり基底クラスにキャストして配置するのは、base クラスに何らかのメモリレイアウトの仮定が必要じゃないでしょうか?  実際、その placement new を行うために、base (や、child も?)に課される条件は何かありますか?  それとも、base がどんなクラスだったとしても、placement new は未定義動作を含まず、base 部に配置されますか? 実際の所、①や②の方法で出来れば、現実問題の解決としてはそれで良いのですが、ところで最初の質問の、修正後のコードのような基底クラスの複数回の呼び出しが、未定義動作かどうかが気になっていますので、そちらについてご回答いただけるとありがたいです。 よろしくお願いします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問