4

警告: この質問は、6 年前に研究プロジェクトとして他の誰かによって書かれた、適切なドキュメントなしで、ひどいコードの山を処理しなければならなかったために生じました。明らかに、より良い解決策は、適切な設計により、これらの問題を最初から引き起こさないことです...

とはいえ、問題は次のとおりです。このような状況から抜け出すための最良の方法は何ですか。

  1. クラスはヒープにメモリを割り当て、デストラクタで解放します。
  2. どこかで、クラスのインスタンスがグローバル スコープで宣言されています。
  3. このインスタンスを初期化する関数が存在します。
  4. その関数の戻り値は、静的変数を初期化するために使用されます。
  5. グローバル スコープ変数は、静的スコープ外で使用されます。

最小限の実例:

ファイル "myclass.h":

#ifndef MYCLASS_H
#define MYCLASS_H

#include<vector>
using namespace std;

class myclass{
        vector<int> *onTheHeap;
public:
        myclass(int len=0){
                onTheHeap = new vector<int>(len);
        }

        ~myclass(){
                delete onTheHeap;
        }

};

#endif

ファイル「static_loader.cpp」

#include "class.h"
myclass existsForever;

int cause_static_global_creation(){
        existsForever = myclass(5);

}

static int bootstrap = cause_static_global_creation();

およびファイル「main.cpp」:

#include "class.h"

extern myclass existsForever;

int main(){
        return 0;
}

ビルド:

g++ -g -c static_loader.cpp
g++ -g main.cpp static_loader.o

次のように実行します。

 valgrind --leak-check=full ./a.out

結果: 変数は、変数のデストラクタが終了ハンドラの main の下で呼び出されたときに解放されますが、static_loader から main の下の static_initialization_and_destruction_0 関数でも呼び出されます!

コードを大幅にリファクタリングせずに、これらの変数を 1 回だけ確実に解放する方法はありますか? 私が使用しなければならないライブラリには、このパターンのインスタンスが数十あります...

編集:

機能の追加:

    void operator=(myclass other){
            delete this->onTheHeap;
            this->onTheHeap = other.onTheHeap;
    }

    myclass(const myclass& other){
            this->onTheHeap = new vector<int>(*(other.onTheHeap));
    }

動作を変更しません。

2番目の編集:

    myclass& operator=(const myclass& other){
            delete this->onTheHeap;
            this->onTheHeap = new vector<int>(*(other.onTheHeap));
            return *this;
    }

すべての問題を解決します。とにかく、私のライブラリにはこのようなソースでメモリリークがありますが、それを再現する方法がわかりません. 少なくともこれではありませんが、リファクタリングなどの提案にも感謝します!

4

3 に答える 3

2

あなたの仮定は破られています。myclass existsForever;によってではなく、によって初期化cause_static_global_creationされmyclass::myclassます。代わりにcause_static_global_creation、すでに初期化されているオブジェクトに値を割り当てます。

また、クラスは三つのルールに違反しているため、割り当てによって問題が発生するのは当然のことです。

于 2012-10-22T12:48:43.807 に答える
1

myclassあなたの例では2回構築されています。

まず、len=0ステートメントによってmyclass existsForever;

その後、cause_static_global_creationwithで一時的なインスタンスが作成されlen=5、デフォルトの代入演算子 to を使用して割り当てられますexistsForever。この時点でexistsForever、テンポラリはonTheHeapポインタの同じ値を共有します。テンポラリは ですぐに破棄されcause_static_global_creation、ベクター用にメモリが解放されます。グローバルインスタンスが破棄されると、プログラムの終了時に同じメモリが再び解放されます。

それをすばやく修正する方法をいくつか提案します。

1.コンストラクタを次のように定義します

myclass(int len=0)
{
  if ( len > 0 )
    onTheHeap = new vector<int>(len);
  else
    onTheHeap = NULL;
}

2.ベアポインタの代わりにスマートポインタを使用してください。

std::shared_ptr<vector <int>> onTheHeap;

3. ヒープ上にベクターを構築しないでください。代わりにインスタンス メンバーを使用してください。

より長い方法は、代入演算子とコピー コンストラクターを適切に実装することです。

于 2012-10-22T12:46:14.210 に答える
1

これらのコメント/戦略はあなたの状況をカバーしていると思います:

  1. class.h を所有している場合は、 を に置き換えてvector<int>*くださいvector。コンパイラがスタック メモリの管理を行い、ヒープ リークを回避します。
  2. グローバルな静的は、プログラムの実行中にスタック領域を消費することに注意してください。そのため、バイトごとにこれは動的メモリリークと同じくらい「悪い」ものです。
  3. Benjiがデフォルトのコピーctorを介して指摘したように、二重削除はおそらくポインターエスケープへの参照によって引き起こされます。これを共有ポインター (delete を呼び出す必要がない) に置き換えることもできますが、スタックの方が優れています。コピー コンストラクターを無効にするか、浅いコピーではなくディープ コピーを行うコピー ctor を作成することを検討してください。Benji がさらに指摘したように、代入とコピー構築を無効にしてコンパイルしない場合は、問題 (の 1 つ) が見つかりました。
  4. グローバルな静的は、それらがコレクションであり、使用後にジャンクが削除されることなく無限に拡大し続けない限り、メモリの使用において問題になることはありません。内部のベクトルがクリーンアップされずに無期限に拡張されない場合、これらはプログラムの実行に関して一定量のメモリを消費します。これらの寿命を短くすることで、コードの因数分解が改善されますが、懸念事項がメモリである場合は、時期尚早の最適化と見なされます。
于 2012-10-22T12:46:50.093 に答える