newするかどうか

[戻る]
tonoma 2002/07/18(木) 01:06:33
漠然とした質問なのですが

newするかどうかはどのようにみなさん、決めていらっしゃいますか?

たとえば、CHogeというクラスがあったとして

CHoge *hoge = new CHoge();
hoge->Method();
delete hoge;

でしょうか。

それとも

CHoge hoge;
hoge.Method();

でしょうか。

うまく言えないのですが、ただ上記処理を行うだけであれば
deleteの手間がない分、後者の方がが楽だしすっきりします。
でも、上記で書く人はいます。
上記で行う理由が知りたいのです。
漠然としていますが、お願いします。

環境:Win2000 VC6.0 MFC

tonoma 2002/07/18(木) 01:29:09
もしかして
CHogeクラスのコンストラクタには必ず引数が必要だった場合
つまり、コンストラクタが
CHoge(int x)
となっていて
デフォルトコンストラクタがない場合とかは、

クラスのメンバ変数として、CHogeを使いたくても
そのメンバとして、
class CTest {
    CTest CTest();

    CHoge m_hoge(5);
};
とかできない。

だから
class CTest {
    CTest CTest();

    CHoge *m_hoge;

    void a(){
        m_hoge = new CHoge(5);
    }

    void b(){
        delete m_hoge;
    }
};
というように使うのか?

もしこのような理由だったら大した理由ではないですね。
でもこうじゃないですよね。
う〜ん、悩みは深まるばかり。

PAI 2002/07/18(木) 02:33:30
デフォルトコンストラクタなくてもメンバ初期化子で初期化できますよ。
この例の場合

CTest::CTest():m_hoge(5){    }

とすれば。
(初期化に使う値がほかのメンバの初期化後の状態に依存するとかあると
 いろいろ気をつけなきゃいけないことがでてきますけど)


newで作るかスタックフレームに作るかですけど、
ローカルでスタックに作るとそのオブジェクトの寿命が管理できない
(っていうか勝手に管理される)から、
それが嫌なときヒープに作るってことでいいんじゃないでしょうか?

あと、ローカルに作るときはサイズが決まってる必要がありますよね。
実行時にならないとサイズが決まらない場合はヒープに作ってやる必要がありますよね。

あ、それとスタックはヒープに比べて小さいから、
大きなオブジェクトを作んなきゃなんない時はヒープに作るか、
staticにしてやるかしてやんなきゃいけませんね。

まとめると、

基本はスタック領域で
・寿命を管理したい。
・コンパイル時に大きさがわからない。
・スタックに置くには大きい
とき、ヒープに確保する。

ってところでしょうか?

tonoma 2002/07/18(木) 03:08:04
PAIさん、返答ありがとうございます。

CTest::CTest():m_hoge(5){    }さん。
↑これ知らなかったです。というか見たことはあるような気がしますが。

>基本はスタック領域で
>・寿命を管理したい。
>・コンパイル時に大きさがわからない。
>・スタックに置くには大きい
>とき、ヒープに確保する。
ですが
寿命を管理したい。場合は、
メンバにもつと、CTestの寿命と同じになる。
newすると、a()を呼んで生成される。b()で消滅になるんですね。ふむふむ。

コンパイル時に大きさがわからない。
については、例えばCHogeが10個かもしれないし、100個かもしれないという
場合についてですよね。

スタックに置くには大きい。
ですが、これはほとんど考えたことがなかったです。
これは、sizeof(CHoge) (×CHogeの個数) が大きいか小さいかということですね。
メモリがたくさん使える環境になれすぎてあまり気にしたことがなかったです。


ところで、寿命がCTestクラスと同じ、CHogeは1つしか作らない、
スタックに置いても問題ない大きさの場合は
無条件に、newしないということにならないでしょうか。
揚げ足をとるわけではないのですが、上記条件でもnewしている方は
いらっしゃいますよね。
これがまた謎で。この問題は実は何年も悩んでいます。

またさらに考えてみたのですが
クラス同士の関連を実現するのに、関連するクラスのポインタを保持することによって
実装というのはよくありますよね。

このケースの場合にnewする場合もあるのではないでしょうか。
メンバにただ持つだけだけだと、集約になってしまう!?

思いつきで書いてみました。

PAI 2002/07/18(木) 04:35:11
>上記条件でもnewしている方はいらっしゃいますよね。

関数の頭でnewして、末尾でdeleteしてたりするんしょうか?
newしていけない理由はないし・・・趣味じゃないでしょうか??
ほかに何か理由があるんでしょうか?あれば知りたいです。


>クラス同士の関連を実現するのに、関連するクラスのポインタを保持することによって
>実装というのはよくありますよね。

これはちょっと複雑ですけど。
まず、ポインタを持つ理由には依存関係をへらすという意味もあったりします。

クラスAの実体をクラスBの中に持とうとすると、
クラスBの宣言時にクラスAの内部構造が(コンパイラに)わからなければなりません。
クラスBがクラスAに依存してしまうわけです。
ポインタだけを持たせるのであれば依存関係は生じません。

クラスAの中にクラスBがあり、かつクラスBの内部にクラスAがある、
という状態にすることはできませんので、この場合ポインタを持たせるしかないですよね。


>このケースの場合にnewする場合もあるのではないでしょうか。

これは寿命管理の問題そのものですよね。
たいていオブジェクトが協調しあう場合、そのオブジェクトコンポジションは
ひとつのスコープに収まっているものではないんで。
(いや、ひとつのスコープに収まっていていけない理由はないんですが・・・)
初期化関数でオブジェクト同士を結び付け、何か作業をしたあと、後始末関数で開放する。
といった流れがあったとき、スタック上のオブジェクトで
オブジェクトコンポジションをやろうとすると
全体をひとつの関数内に入れて、そこのローカルで全部確保するしかなくなってしまいます。

PATIO 2002/07/18(木) 10:46:11
私もPAIさんの意見に賛成ですね。

・寿命を管理したい。
・コンパイル時に大きさがわからない。
・スタックに置くには大きい
とき、ヒープに確保する。

で良いと思います。
あとは、その人の趣味ということになると思いますよ。
私は上記に当てはまらない限りはnewしないことにしています。
これは、楽だからではなくてnewとdeleteを不必要に繰り返すと
メモリの断片化が起こりそうでいやだからです。

メンバー変数に持つかどうかに関してはそのクラスの性質や使われ方も
絡むので一概には言えないでしょう。それこそケースバイケースの話なので。

古いスレッドには追加発言できません。