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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C++

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

Q&A

1回答

13476閲覧

クラスのインスタンスをメンバに持つクラスについて(C++)

mentos109

総合スコア28

C++

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

0グッド

0クリップ

投稿2016/10/10 15:07

現在C++を勉強中なのですが、下記のようなコードを書いたところ、"クラス"hoge"の規定のコンストラクターは存在しません” "'hoge':クラス、構造体、共用体に規定のコンストラクターがありません。"というエラーが出ます。

C++

1class hoge { 2public: 3 hoge(int i, int j) { this->i = i; this->j = j; } 4private: 5 int i; 6 int j; 7}; 8 9class hoge2 { 10public: 11 hoge2(hoge) { this->hoge = hoge; } //←ここの{にエラーが出る 12private: 13 hoge hoge; 14};

ですが、このように書くとエラーが出ませんでした。

C++

1class hoge { 2public: 3 hoge(int i, int j) { this->i = i; this->j = j; } 4private: 5 int i; 6 int j; 7}; 8 9class hoge2 { 10public: 11 hoge2(hoge) : hoge(hoge) {} 12private: 13 hoge hoge; 14};

これはなぜなのでしょうか?よろしくお願いします。

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

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

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

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

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

guest

回答1

0

こんにちは。

「規定のコンストラクター」は引数無しで呼び出せるコンストラクタです。
今回のケースでは、例えば、下記のようなコンストラクタが規定のコンストラクターです。

hoge() { i=0; j=0; }

コンストラクタを1つも定義しない時だけ、デフォルト・コンストラクタは自動生成されますが、既に別のコンストラクタを定義しているのでhogeクラスはデフォルト・コンストラクタが自動生成されません。

そして、前者のhoge2(hoge) { this->hoge = hoge; }は、コンパイラが自動的に補完して、
hoge2(hoge) : hoge() { this->hoge = hoge; }となります。
このhoge()はデフォルト・コンストラクタを呼び出しますが、ないのでエラーになります。

次に、コピー・コンストラクタは特殊なケースを除き、自動生成されます。今回は特殊ケースに該当しないため、自動生成されています。
後者のhoge2(hoge) : hoge(hoge) {}のhoge(hoge)は他に適合するコンストラクタがないのでコピー・コンストラクタを呼び出します。そして、コピー・コンストラクタは自動生成されているのでエラーになりません。

興味があるようでしたら、コンストラクタが暗黙に宣言されるとき、されないときが比較的分かりやすいと思います。(でも、この辺は本当に難しいです。)


【ところで】
個人的にはthis->xxxでメンバ変数であることを示す記述はバグの元と考えています。
何と言っても視覚的に読みにくいです。->は最小限にしたいものです。

そして、例えば、下記のようなケースでthis->を書き忘れたら、なかなか痛いです。

C++

1class foo 2{ 3 int bar; 4 : 5 void setup(int bar, int baz) 6 { 7 this->bar=(baz)?bar:baz; 8 } 9};

私なら下記のように記述します。

C++

1class foo 2{ 3 int mBar; 4 : 5 void setup(int iBar, int iBaz) 6 { 7 mBar=(iBaz)?iBar:iBaz; 8 } 9};

プリフィックスで変数の種別を分けてます。引数とメンバ変数を区別するための命名規則は人により、プロジェクトにより異なりますが、引数とメンバ変数に付ける名前を変えるケースは(C++界隈では非常に)多いです。

投稿2016/10/10 16:21

Chironian

総合スコア23272

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

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

mentos109

2016/10/10 16:57 編集

回答ありがとうございます。 少し調べてみて、宣言部分と定義部分の間?に: hoge(hoge)などのように書いてメンバ変数の初期化を行うことをメンバイニシャライザということはわかったのですが、この時の処理はどのようになってるのでしょうか?コピーコンストラクタを呼び出す際のhoge(hoge)と同じ扱いなのでしょうか?もしそうだとすると組込み型の変数?(intなど)では同じことを書くとエラーになるのでメンバイニシャライザでは組込み型の変数の初期化はできないような理屈になるような気がするのですがそういう訳ではないようで混乱しています。 同様の理由で、前者をコンパイラが補完してhoge2(hoge) : hoge()になるとデフォルトコンストラクタが呼び出されるのも(メンバイニシャライザでの処理がよくわからないので)正直なぜだかわかりません。 また、hoge2(hoge)がコンパイラによってhoge2(hoge) : hoge()になるということは、コンストラクタにかぎらず、引数に特定のクラスを取る関数すべてでそのようになってしまうということなのでしょうか?もしそうなのだとしたらすべてのクラスでデフォルトコンストラクタを定義するべきなのでしょうか?それともコンストラクタ限定で起きる処理なのでしょうか? ->ですが、なるほどわかりました。 変数名を考えるのが面倒でthis->を使用していましたが、これからは区別していこうと思います。
Chironian

2016/10/11 06:10

> コピーコンストラクタを呼び出す際のhoge(hoge)と同じ扱いなのでしょうか? その通りです。 > もしそうだとすると組込み型の変数?(intなど)では同じことを書くとエラーになる エラーにはならないようですよ。 hoge(int i, int j) { this->i = i; this->j = j; } を下記のように書いてみました。msvc, mingw(gcc)の両方で通りました。 hoge(int i, int j) : i(i),j(j) { } しかし、hogeはクラスなので何故組み込み型が関係するのか、良く分かりませんでした。もしかして、下記のように書いてエラーになりましたか? hoge() : i, j { } 正しくは、下記のように書きます。 hoge() : i(), j() { }  、もしくは、 hoge() : i(0), j(0) { } > 正直なぜだかわかりません。 特殊なことをしない限り、クラスのメモリが獲得される時には必ずどれかのコンストラクタが呼ばれるのがC++の仕様です。(でないとメモリ管理が大変なことになります。) 明示的にコンストラクタを指定していない時は、引数の指定がないため、呼ぶことができるのは引数が不要なデフォルト・コンストラクタだけですね。なので、明示的に指定してない時はデフォルト・コンストラクタが呼ばれます。 メンバ・イニシャライザは、呼び出すコンストラクタを明示的に指定する唯一の方法です。 なお、残念ながら組み込み型は初期化されない時も少なくありません。C言語との互換性の問題だろうと理解してます。 > それともコンストラクタ限定で起きる処理なのでしょうか? その通りです。 上述したようにクラスのメリモを獲得する時になんらかのコンストラクタが呼ばれます。あるクラスのコンストラクタが呼ばれた時、そのクラスに含まれるクラスも同時にメモリが獲得されますから、その含まれる側のクラスのコンストラクタも呼ばれます。 > 変数名を考えるのが面倒でthis->を使用していましたが、これからは区別していこうと思います。 変数名考えるの面倒ですよね。 なので私は自分なりに命名規則を決めました。 メンバ変数はmで始め、引数は入出力に応じてi, o, ioで始めるなどです。具体的な決め方は人によりプロジェクトにより様々ですが、決めておけば引数とメンバ変数で一々新たに考案しなくて良いので楽ですよ。
mentos109

2016/10/11 06:25

返信ありがとうございます。 すみません、少しわかりにくいところがありました。 >しかし、hogeはクラスなので何故組み込み型が関係するのか、良く分かりませんでした。 これは、メンバイニシャライザでのhoge(hoge)という書き方が、コピーコンストラクタを用いてインスタンスをコピーする際のhoge(hoge)と同じ書き方であるので、コピーコンストラクタを呼び出すためにメンバイニシャライザではこのような書き方になってるのではないのか、でももしそうだとすると通常のmain文の中などで、int型の変数をコピーするためにint i(j);などとするとエラーが出るのでやっぱり違うのかな、という疑問でした。(これでもわかりづらい気がします(汗) また、これも少しまだよくわからないので聞き直したいのですが、 クラスのメモリが獲得される時かならずコンストラクタが呼び出されるのなら、 例えば通常の関数でクラスを引数に取る場合、 void func(hoge hoge) {} このような場合はエラーが出ないのですが、これはなぜなのでしょう? この場合はコンストラクタは呼び出されていないということなのでしょうか? そしてコンパイラがhoge2(hoge){}をhoge2 : hoge(){}と自動で補完するのはなぜなのでしょう? くどくて申し訳ありません。よろしくお願いします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問