2

再帰的な C++ プログラムでメモリの割り当てと割り当て解除に問題があります。自動メモリ管理ソリューションを使用せずに、誰かが私が経験しているメモリリークを解決するのを手伝ってくれるのではないかと思います.

次のコードは基本的に問題を説明しています (これは不自然な例ですが、間違いや簡略化を修正してください)。

数値の値を保持する数値クラス:

class Number {
    public:
        Number() { value = 1; };
        Number& operator + (const Number& n1) const {
            Number result = value + n1.value;
            return result;
        };
        int value;
};

再帰を実行する 2 つの関数:

Number& recurse(const Number& v1) {
    Number* result = new Number();
    Number one = Number();
    *result = *result + recurse(one);
    return *result;
}

int main(...) {
    Number answer = Number();
    answer = recurse(result);
}

ご覧のとおり、再帰関数で割り当てられたメモリがリークされていますが、再帰の性質に基づいて、このメモリをどこから解放できるかわかりませんか?

4

5 に答える 5

13

問題はここにあります:

Number& operator + (const Number& n1) const {
    Number result = value + n1.value;
    return result;
};

result参照によってローカル変数 ( ) を返していますが、これは大きな NO-NO です。ローカル変数はスタックに割り当てられ、関数が終了すると変数はなくなります。ローカル変数への参照を返すことは、現在別の目的で使用されているスタックへのポインタを返すことであり、多くの悪さを引き起こすことになります。

代わりにすべきことは、値で返すことです (戻り値の型を からNumber&に変更するだけNumberです)。適切なコピー コンストラクターがあること、またはコンパイラーの自動生成されたコピー コンストラクターがニーズに合っていることを確認してください。つまり、operator+返されるときにコピーが作成され (多くの場合、最適化によって削除されます)、ポインターや参照が含まれないため、破損した戻り値を取得することはできません。

メモリ リークを修正するには、 などのスマート ポインターを使用できますboost::shared_ptr。または、ポインターと動的メモリを完全に捨てて、結果を からの値で返すだけrecurse()です。

于 2009-01-07T22:18:43.560 に答える
3

そもそもヒープにメモリを割り当てている理由がわかりません:

Number& recurse(const Number& v1) {
    Number result;
    Number one;

    // I assume there is a step here to determine if the recursion should stop

    result += recurse(one);
    return result;
}

スタックのみに割り当てることで、関数が戻るときに変数がクリーンアップされることが保証されます。

それ以外の場合は、ある種のスマート ポインターを使用する必要があると思います。

于 2009-01-07T22:21:26.753 に答える
2

したがって、Adam Rosenfieldが指摘したローカル変数のアドレスを返す以外に、コードに3つの問題があります。

まず、あなたの復活機能は決して終わらないでしょう。recurse()のある時点で、recurse()を再度呼び出さずに戻る値を確認する必要があります。それが再帰の基本的な部分です。渡された引数v1も使用されていません。

次に、operator +()は実際には機能しません。Number()オブジェクトにintを割り当てる方法はありません。

第三に、主に、宣言されない結果と呼ばれるものを渡します。

これらのエラーを忘れて、スタックオーバーフローを回避するために、ヒープ上のすべてのオブジェクトを割り当てたいと思います。この関数は何度も繰り返されるか、実際に使用されるオブジェクトがNumberよりもはるかに大きくなります。その場合、recurse()内のヒープに戻り変数を割り当てることにより、呼び出し元に返されたオブジェクトを削除するように強制します。したがって、recurse()およびmain()でrecurse()を呼び出した後、戻り値を削除する必要があります。呼び出し元にそのことを示すための規則は、参照ではなくポインターを返すことです。したがって、recurse()とmain()は次のようになります。

Number* recurse(const Number& v1) {
    Number* result = new Number();
    Number one;
    if(v1.value >= 2) {
        Number temp;
        temp.value = v1.value - 1;
        Number* partialResult = recurse( temp ); //capture the object to delete
        *result = *partialResult + one;
        delete partialResult;                    //delete the object
    }
    return result;
}

int main() {    
    Number result;
    result.value = 15;
    Number *answer;
    answer = recurse(result);
    delete answer;
}

注:recurseが実際に計算するものは、無意味です。意図はわかりませんが、それはうまくいくものです。

于 2009-01-07T23:07:31.323 に答える
1

メモリを動的に割り当てている理由はありますか?

Number recurse(const Number& v1) {
    Number result;
    Number one;
    retun result + recurse(one);
}

また、値 v1 を使用していないことに気付きました

しかし、大きな間違いは、再帰にエスケープ句がないことです。
これは事実上、基本的にメモリ不足になる無限再帰です。

于 2009-01-07T23:17:13.630 に答える
0

スマートポインタはあなたの友達です。少なくともauto_ptrをすばやく読んでください。

また、他の問題に関するAdam Rosenfieldのコメントを読んでください(もう存在しない値への参照を返します)。

于 2009-01-07T22:26:47.070 に答える