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

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

詳細はこちら
C++

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

Q&A

解決済

2回答

2685閲覧

C++でクラスのメンバ変数に、クラスの配列を取る方法

the_hoots_under

総合スコア33

C++

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

0グッド

0クリップ

投稿2019/09/23 02:55

久々にC++でクラスを作ろうと思ったらすっかり忘れてしまいました。
次のようなクラスを作りたいのですが、どうように作ればいいのか思い出せません。

子供クラス:Child,親クラス:Parentを定義します。
親クラス:Parentのメンバ変数として、複数のChildインスタンスを取りたいです。
ただし、Parentのインスタンスを生成した段階では、Childのインスタンスを何個取るか決めていません。

Parentのインスタンスを生成したのち、適当なメンバ関数に子供クラスの数N_childrenを引数として、N_children個のChildクラスを親クラスに持たせます。

適当な処理をしたのち、親クラスが持っていた子供クラスのインスタンスのメモリを解放します。

(または、親クラスのインスタンスを解放をすれば、親クラスが持っていた子供インスタンスのメモリも含めて全て解放されるのでしょうか。)

以下はなんとなくのものです。上の質問の補足です。
ざっくりと親クラスに、Childクラスのポインタを持たせておいて、
new でN_children個のChildクラスの動的配列を取るような以下のメンバ関数を作ればいいんですかね。

generate Children(int N_children){ children = new Child[N_children] }

となると、childerenの解放をどうやるのかがわかりません。
普通に、

delete[] children

とすするメンバ関数を作ればいいんですかね。
また、これもできるし、別にこういうことをしなくても、parentのインスタンスを解放すればそれでいいんですかね。

グダグダですいません。とりあえず、こういうクラスをどうやって作るべきか、簡単に教えていただけませんでしょうか。

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

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

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

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

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

guest

回答2

0

ざっくりと親クラスに、Childクラスのポインタを持たせておいて、

new でN_children個のChildクラスの動的配列を取るような以下のメンバ関数を作ればいいんですかね。

子要素管理もvectorあたりに任せたほうがいいのではないかと思います。

投稿2019/09/23 02:58

maisumakun

総合スコア145975

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

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

the_hoots_under

2019/09/23 05:50

記述式が思い出せませんが、 子クラスChildのポインタを引数とするvectorを、親クラスParentにメンバ変数として定義して、 親クラスのインスタンスを生成したのち、vectorをN回push_backする。 その後、全てのvector要素にnew でchildクラスのインスタンスを生成する みたいなことをすればいいんですかね。
the_hoots_under

2019/09/23 05:51

いや、ちょっとよくわからないですね。 最初のpush_backにはnullを入れるんですか?。
guest

0

ベストアンサー

質問中の説明から読み取れる状況から判断すると Parent のデストラクタに delete[] children; を書くだけだけだと思うのですが、更に言えばスマートポインタを使うのがより良いでしょう。

コードで書くとこうなります。

cpp

1#include <memory> 2#include <cstdlib> 3 4class Child { 5}; 6 7class Parent { 8private: 9 std::unique_ptr<Child> children; 10public: 11 Parent(void) = default; 12 void allocation(std::size_t N_children); 13 void process(void); 14}; 15 16void Parent::allocation(std::size_t N_children) { 17 children.reset(new Child[N_children]); 18} 19 20void Parent::process(void) { 21 // 何かの処理 22} 23 24int main(void) { 25 Parent par; 26 par.allocation(10); // 適当な量のメモリを割り当て 27 par.process(); // Parent に何かの処理をさせる 28 // スコープの終わりで par が解体される処理の中で 29 // children (スマートポインタ) も解体されてメモリが解放される 30}

投稿2019/09/23 06:14

SaitoAtsushi

総合スコア5684

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

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

the_hoots_under

2019/09/23 06:40

コードありがとうございます。非常に勉強になります。 こういう書き方があるのですね。スマートポインタを初めて知りました。 質問とは関係のない内容になってしまうのですが unique_ptrも初めて知ったのですが、これは確保したメモリのアクセス権がこのポインタにしかないと書いてありました。てっきり、通常のポインタもそのポインタにしかアクセス権がないと思ってしまっていたのですが、 通常のポインタの場合 int *ptr1,*ptr2; ptr1 = new int; として、ヒープ領域にメモリを確保した後 ptr2 = ptr1; として、一つのヒープ領域のメモリに対し、複数のポインタ参照ができるのでしたか?
the_hoots_under

2019/09/23 06:51

どうして #include <cstdlib> があるのでしょうか。
SaitoAtsushi

2019/09/23 07:25

はい。 生のポインタはあくまでもオブジェクトを指すものであり、唯一の所有者という考え方はありません。 #include <cstdlib> を書いてあるのは、 std::size_t がそこで宣言されているのを使いたかったからです。 cstdio や cstddef でも (同じ内容で) 宣言されていることになっているのでどれでも良いです。 そういうった基本的なヘッダファイルは間接的に include されてしまっていることもあるのですが、 #include <memory> で std::size_t が導入されることが保証されるのかどうか私は仕様から読み取れませんでしたので、念のために include しています。
the_hoots_under

2019/09/23 08:48

std::unique_ptr<Child> children;の部分って std::unique_ptr<Child[]> children; ですか?
the_hoots_under

2019/09/23 09:00

これって普通に children[i]->variable; みたいにメンバ変数にアクセスできんるですか?
SaitoAtsushi

2019/09/23 09:34

そうですね。 この場合は Child[] とすべきでした。 間違いです。 std::unique_ptr クラスは演算子オーバーロードが上手く定義されていて、基本的にはポインタのように使えます。 もちろんデータメンバへのアクセスも。
the_hoots_under

2019/09/23 10:30

正直作成過程でコンパイルができないのですが、 回答者様の通りに、定義すると VScodeのチェック機能だと、エラー判定となってしまいます。 仕方がないので、 Child ** children; というメンバ変数を親クラスにとって children = new Child*[N]; for (){ children[ i ] = new Child; } としました。 普通に、親クラスに複数の子供クラスを持たせようと思ったらこれでいいんですかね。 後、結局親クラスのインスタンスがスコープを抜けて消えるときに、自動的に子クラスのインスタンスも全て deleteしなくてもメモリを解放しますよね。。 すいません。ちょっと久々で。。
SaitoAtsushi

2019/09/23 10:54

いいえ。 delete は必要です。 new で確保されたメモリはどこかで delete する必要があります。 スマートポインタに入れた場合にはスマートポインタ (std::unique_ptr など) のデストラクタの中で delete しているからスマートポインタの解体と同時に指している先も解放されてるのであって、生のポインタはどこかにあるオブジェクトを指すだけです。 生のポインタは指している先のオブジェクトを自動で管理したりはしません。
SaitoAtsushi

2019/09/23 11:10

エラーとはどのようなエラーですか。 どこに対してどのようなエラーだと示されていますか?
the_hoots_under

2019/09/24 11:01

返答が遅れました。 回答者様のコードをいじったら、普通にエラーも出ずに走りました。 私が自分のプログラムのために回答者様のコードを参考に、作成したものはすでに消してしまったので 何を間違えたのかわかりませんが、私の方のミスでした。 お騒がせしまして申し訳ございませんでした。 ただし、クラスポインタの配列要素からそのクラスインスタンスのメンバ変数へのアクセス方法が 例えば children[2] -> menber_variable; ではなく、 children[2].menber_variable; でした。 ここが少し引っかかりました。
SaitoAtsushi

2019/09/24 12:45

ポインタなのは childlen であって childlen[i] の型は Child だからですね。
the_hoots_under

2019/09/25 05:03

なるほど。 少し勘違いをしておりました。 配列自体をポインタで取ると言うことではなくて、 クラスポインタを要素とする配列を取る と言うことを考えておりました。 これがやりたかった理由は 親クラス内部にもつ配列自体を小規模にしたかったためです。 何はともあれ問題が解決したのでよかったです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問