0

私の小さなプログラムでメモリリークを解決できません。一部のコードはもともと Java で作成されていたので、それを C++ に「変換」していました (それらのいくつかは奇妙に思えるかもしれません。より良い解決策があれば教えてください - C++ での OOP は初めてです)。私の意図は、ランダムな高さマップ ジェネレーターを作成することです。2 つのメモリ リークがあります (Visual Leak Detector で検出):

最初のものはここでトリガーされます:

-> Mountain* mount = new Mountain(size, Utils::powerOf2Log2(size) - 6, 0.5f, seed);
   ChannelClass* height = mount->toChannel();

このため、「Mountain」クラス コンストラクターでは次のようになります。

channel = new ChannelClass(size, size);

私は次のようなシャットダウン方法を使用しようとしていました:

mount->ShutDown();
delete mount;
mount = 0;

Shutdown() が次のように定義されている場合:

if(channel){
    channel->ShutDown();
    delete channel;
    channel = 0;
}

「ChannelClass」の ShutDown() メソッドが float 配列を削除しています。私の最初の考えは、「ChannelClass* height = mount->toChannel()」が問題を引き起こしている可能性があるということでした。

さらにコードが必要な場合は、お知らせください。喜んで手伝ってくれてありがとう!

4

1 に答える 1

6

OK、これ以上のコードがなければ、これはかなり一般的なものになります。これらはガイドライン (ルールではありません) であり、最も優先度の高いものが最初になります。

最初に、C++11 に関する簡単なメモ: 持っていない場合は、std::unique_ptr以下をstd::auto_ptr(理由により非推奨になっているため注意してください) に置き換えるか、代わりにboost::scoped_ptrを使用してください。

1.使わないnew

(単一の)山を作成する必要があり、それが宣言されているスコープ外で存続させる必要がない場合は、自動スコープで通常の変数として使用してください。

void automatic_scope(int size, double seed)
{
    Mountain hill(size, Utils::powerOf2Log2(size) - 6, 0.5f, seed);
    // ... mountainous operations happen here ...
}   // hill is destroyed here - is that ok for you?

同様に、山が単一の ChannelClassを所有し、それを所有する山とまったく同じくらい存続する必要がある場合は、次のようにします。

class Mountain
{
    ChannelClass channel;

public:
    Mountain(int size, int powerthing, double something, double seed)
    : channel(size, size) // initialize other members here
    {
        // any more initialization
    }

    ChannelClass& toChannel() { return channel; }
};

これで、ChannelClassは とまったく同じように存続しMountain、すべてが自動的に破棄され、明示的なシャットダウンは不要になります。

2.使用しないでくださいnew[]

同様に、範囲が限定された複数の山が必要な場合は、

void automatic_scope_vector(int size, double seed)
{
    std::vector<Mountain> hills;
    hills.push_back(Mountain(size, Utils::powerOf2Log2(size) - 6, 0.5f, seed));
    // ... mountainous operations happen here ...
}   // hills are all destroyed here

3. よし、newやっぱり使う

明らかに、new を使用する正当な理由があります。その 1 つは既に言及されています (山を作成したブロックよりも長く維持する必要があります)。

もう 1 つは、実行時のポリモーフィズムが必要な場合です。たとえば、Mountainまたはのサブクラスが複数あるChannelClassが、基本クラスを扱いたい場合です。

ポリモーフィック ファクトリ関数で両方を説明できます。

class Molehill: public Mountain { ... };
class Volcano: public Mountain { ... };

std::unique_ptr<Mountain> make_mountain(int size, double seed, bool is_molehill)
{
    std::unique_ptr<Mountain> result;
    if (is_molehill)
        result.reset(new Molehill(size, size/2, 0.01f, seed));
    else
        result.reset(new Volcano(size, size*2, 0.5f, seed));
    return result;
}

void automatic_scope_polymorphic(int size, double seed, bool is_molehill)
{
    std::unique_ptr<Mountain> hill = make_mountain(size, seed, is_molehill);
    // ... polymorphic mountainous operations happen here ...
}   // hill is destroyed here unless we gave the unique_ptr to someone else

同様に、山を動的に作成ChannelClass する必要がある場合は、それunique_ptr.

オブジェクトをコピーして渡す必要があり、コピーに非常にコストがかかり、RVO や移動セマンティクスに依存できない (またはまだ持っていない) 場合にも役立つ場合があります。ただし、これは最適化であるため、プロファイリングで問題が示されない限り、心配する必要はありません。


哲学

これらの C++ イディオムはすべて決定論的破壊に基づいており、明示的なクリーンアップ コードの記述をまったく回避することが目標です。

メモリー管理をコンテナー ( などstd::vector) およびスマート・ポインター ( などstd::unique_ptr) に委譲することで、Java がガーベッジ・コレクションで対処するメモリー・リークを回避できます。ただし、同様の自動スコープ ガード オブジェクトがメモリだけでなくすべてのリソースの管理を自動化できるRAIIに強力に一般化されます。たとえば、関数に複数の戻りパスがあり、例外がスローされる可能性がある場合でも、mutex ロックが正しく解放されるようにします。std::lock_guard

明示的なクリーンアップ コードを記述する必要がある場合: 呼び出さなければならないカスタム シャットダウン メソッドを記述しないでください。それをdestructorに入れるだけです。可能であれば、これを低レベルのガード オブジェクトにもプッシュします。

于 2012-08-21T18:33:58.603 に答える