複数のクラスを基底クラスのポインタで管理

解決


liz  2010-05-21 03:09:57  No: 71648  IP: [192.*.*.*]

Objectというクラスから派生した複数のクラスをObject *で管理したい。
そこでvectorを使ってvector<Object *>で行こうと思ったけど、
格納したオブジェクトがdeleteされた場合にそのポインタをvectorから消し去る方法が分からない・・・。

環境上使えるライブラリが限られてるのでboostはナシで。
できればSTLのみで・・・
うまく実装する方法は無いでしょうか。

編集 削除
tetrapod  2010-05-21 06:31:49  No: 71649  IP: [192.*.*.*]

何がしたいのか具体的によくわからないけど
p=new ObjectA; v.push_back(p);
q=new ObjectB; v.push_back(q);
delete p;
のようになることがありうる、と?

単純にバグの元でしかないので根本的に設計を見直すほうがいい。

編集 削除
tetrapod  2010-05-21 07:15:37  No: 71650  IP: [192.*.*.*]

ああ、見直すのは vector 外で delete p; がありうる、という構造のことで
多態クラスを保持するのに vector<base*> を使うのはしょうがない。

shared_ptr など自作してもたいした手間ではないので自作すれば?
boost からそこだけ持ってきてもいいし。

編集 削除
Ban  2010-05-21 07:53:10  No: 71651  IP: [192.*.*.*]

> 環境上使えるライブラリが限られてるのでboostはナシで。
この理由が著作権/ライセンスの問題なら、
> boost からそこだけ持ってきてもいいし。
はアウトでしょう。
環境が分かりませんが、例えば最近のVCとかだとstd::tr1::shared_ptr
使えたりとかしますが、そういうのはどうですか。

> shared_ptr など自作してもたいした手間ではないので自作すれば?
そういうのもなければこれで。

編集 削除
仲澤@失業者  2010-05-21 18:09:58  No: 71652  IP: [192.*.*.*]

他者から知らぬ所で配列内のオブジェクトを削除されたくないのなら、
その配列自身が、そのオブジェクトの寿命を管理すればよいのでは
ないでしょうか、「ファクトリ付きのコンポジット配列」みたいな
構成になると思います。operator[]()など、配列内オブジェクトを
戻す必要がある場合はconstなオブジェクトか、複製しか戻せな
くなりますけどね(vv;)。
別な方法としてデストラクタをprotectedにする案も考えられますが、
あんまり、見ませんねぇ。もちろん、この場合ファクトリまたは、
配列などのオブジェクトの管理者をfriendにするなど、
やや面倒になります。

編集 削除
liz  2010-05-24 02:40:16  No: 71653  IP: [192.*.*.*]

tetrapodさん
>q=new ObjectB; v.push_back(q);
>delete p;
>単純にバグの元でしかないので根本的に設計を見直すほうがいい。

知り合いに聞いてみた所、シングルトンな管理用クラスを作って
Objectのコンストラクタで管理用クラスにv.push_backをお願いしに行くような
形にして、Objectのデストラクタで同じように管理用クラスにv.eraseをしてもらうようにする方法を提案されたのですが。
(Objectのインスタンス生成時に管理用クラスのポインタをメンバ変数に保持しておく)

それプラス、管理用クラスにaddObject() removeObject()という関数を用意しておいて、そちらでも管理の登録/解除をできるような形を考えたのですが、そういう手法はNGなのでしょうか。(v.eraseはイテレータを回してアドレスで対象を探す感じで)
趣味プログラマでノウハウが全然無いので、教えて頂けると嬉しいです。


Banさん
>環境が分かりませんが、例えば最近のVCとかだとstd::tr1::shared_ptr
>使えたりとかしますが、そういうのはどうですか。

ありがとうございます。そちらも検討してみます。


仲澤@失業者さん
>他者から知らぬ所で配列内のオブジェクトを削除されたくないのなら、
>その配列自身が、そのオブジェクトの寿命を管理すればよいのでは
>ないでしょうか
僕のレス上記参照お願いします。このような手法はNGなのでしょうか。教えて頂けると嬉しいです。


>別な方法としてデストラクタをprotectedにする案も考えられますが、
そこまで大がかり(?)にしたくない、ので、shared_ptr的な実装もしくは
vectorを保持した管理用クラスに管理を依存する実装にしたいと思っています。



以上、レス遅れて申し訳ありませんでした。
返信して頂けるとありがたいです。

編集 削除
仲澤@失業者  2010-05-24 11:21:03  No: 71654  IP: [192.*.*.*]

>僕のレス上記参照お願いします。このような手法はNGなのでしょうか。教えて頂けると嬉しいです。

似た案だと思います。シングルトンに寿命管理させる必要があると
いうことは、配列間での配列エントリ(オブジェクトのポインタ)の
やり取りが発生する、又は、配列外でデストラクタを実行したい
ということですね。
自分の案は、配列外では配列エントリのデストラクタは実行させない
ことに主眼を置いたものです(コンストラクタは管理しなくても良い)
ので、よりライトな実装(ややリスク有り)です。

編集 削除
maru  2010-05-24 17:18:04  No: 71655  IP: [192.*.*.*]

> 格納したオブジェクトがdeleteされた場合にそのポインタをvectorから消し去る方法が分からない・・・。
ところで、消し去った後のvectorはどうなることを想定してますか?
そういう意味で本当にvectorでいいんですか?

編集 削除
liz  2010-05-25 18:35:03  No: 71656  IP: [192.*.*.*]

仲澤@失業者さん
ありがとうございます。僕の案だとやっぱりちょっと重い感じがするので
仲澤@失業者さんの案をベースに検討していこうと思います。

maruさん
>ところで、消し去った後のvectorはどうなることを想定してますか?
>そういう意味で本当にvectorでいいんですか?

申し訳ありません。技量不足で仰っている意味がよく分からないのですが。。。
消し去った後はそのまま、vectorに残っているクラスを管理して
そのポインタでメンバ関数を呼んだりするつもりですが。
というかvectorじゃなくてsetでも十分だと思うのでそっちにスイッチするつもりですが、そう言う事でしょうか?


一応、仲澤@失業者さんの案をベースに実装していく方針にしましたので、
問題は解決したということで、解決チェックをさせて頂きます。
しばらくの間はこの質問は定期的にチェックするので、maruさん、レス頂けると嬉しいです。

編集 削除
liz  2010-05-25 18:36:10  No: 71657  IP: [192.*.*.*]

解決チェック忘れ・・・^^;

編集 削除
liz  2010-05-25 18:38:49  No: 71658  IP: [192.*.*.*]

>というかvectorじゃなくてsetでも十分だと思うのでそっちにスイッチするつもりですが、そう言う事でしょうか?

あー、この部分は忘れて下さい。自分のソースの別の部分と勘違いしてました。

編集 削除
maru  2010-05-26 14:26:43  No: 71659  IP: [192.*.*.*]

> 申し訳ありません。技量不足で仰っている意味がよく分からないのですが。。。
消したところにはnullポインタを格納するの?
ということは、vectorの要素をとってきた後nullチェックをする必要があるので、効率が落ちない?
こういう場合、オブジェクトを管理するコレクションは有効なオブジェクト
だけを格納するようにしたほうがいいので、listなどのほかのものを使った
方がいいんじゃないのかな?
と思っただけです。

編集 削除
liz  2010-05-26 19:02:05  No: 71660  IP: [192.*.*.*]

>消したところにはnullポインタを格納するの?
>ということは、vectorの要素をとってきた後nullチェックをする必要があるので、効率が落ちない?
>こういう場合、オブジェクトを管理するコレクションは有効なオブジェクト
だけを格納するようにしたほうがいいので、listなどのほかのものを使った
方がいいんじゃないのかな?

消した後にremove()とerase()で切り詰めてしまおうかと考えていたのですが
これは手法としてはダメなんですかね・・・?
消されたかどうか、消されたオブジェクトまで把握するつもりはなくて、
有効なオブジェクト(=vectorに格納されているオブジェクト)にだけ、
アクセスできればOKなのですが。

となるとstd::setを使った方が良いような気もしますが・・・
もう何が何だか(汗

編集 削除
maru  2010-05-27 10:01:25  No: 71661  IP: [192.*.*.*]

> 消した後にremove()とerase()で切り詰めてしまおうかと考えていたのですが
> これは手法としてはダメなんですかね・・・?
消すのが常に最後尾ならあまりコストがかからないだろうが、中間部ならば
データの前詰めを行う必要がある。中間部のデータを削除する必要がある
ならlistを使用したほうが世でしょう。
完全にランダムアクセスならばset。
何かの識別子に関連付けて管理するのならmapを使うのが一般的です。

> もう何が何だか(汗
コレクションにはそれぞれメリット/デメリットがあるので、どのような
アクセスが多いのかを考えて選択する必要があります。

> Objectというクラスから派生した複数のクラスをObject *で管理したい。
> そこでvectorを使ってvector<Object *>で行こうと思ったけど、
デザインパターンのコンポジットパターンなので、一般的な手法ですが、
この複数というのが使用中に増減する場合、vectorを使用するのは管理上
のコスト(実行時間)がかかります。数が少ない分には問題にはなりませ
んが、数が多くなくと問題が発生します。

編集 削除
maru  2010-05-27 11:26:08  No: 71662  IP: [192.*.*.*]

修正。orz
>ならlistを使用したほうが世でしょう。
>ならlistを使用したほうが良いでしょう。

> 数が多くなくと問題が発生します。
数が多くなると問題が発生します。

編集 削除
liz  2010-05-30 16:21:17  No: 71663  IP: [192.*.*.*]

解決済みのスレを度々上げてしまい申し訳ありません。


>消すのが常に最後尾ならあまりコストがかからないだろうが、中間部ならば
データの前詰めを行う必要がある。
なるほど。コストの問題でしたか。


>中間部のデータを削除する必要があるならlistを使用したほうが良いでしょう。
listでいきます!


>デザインパターンのコンポジットパターンなので、一般的な手法ですが、
>この複数というのが使用中に増減する場合、vectorを使用するのは管理上
>のコスト(実行時間)がかかります。数が少ない分には問題にはなりませ
>んが、数が多くなると問題が発生します。
デザパタ、本格的に勉強するようにします。。

maruさんの回答非常に参考になりました。ありがとうございます。

編集 削除
maru  2010-05-31 13:35:25  No: 71664  IP: [192.*.*.*]

> listでいきます!
管理するオブジェクトの順序を考えなくてもよいのなら set でもいいと
思いますよ(というより、set の方がふさわしい)。

検索(削除するには削除するものを探す必要がある)のコスト(計算量)は
list:O(n)=n
set :O(n)=log n  (底は2)
でしょうから、要素数が多いと set の方が速くなるはず。
要素数が少ない場合は list の方が早いでしょう。
この辺りはデザインパターンのよりもっと基本的ことです。
デザパタ以前にアルゴリズムの教科書を一読されることをお勧めします。

編集 削除
maru  2010-05-31 13:37:43  No: 71665  IP: [192.*.*.*]

なぜ送信前にちゃんと確認が出来ないんだ! > 自分  orz

> この辺りはデザインパターンのよりもっと基本的ことです。
この辺りはデザインパターンよりもっと基本的なことです。

編集 削除