5

s1.printVal次のコードで、ダングリング ポインター エラーが発生するのはなぜですか? s1オブジェクト、つまりそのポインターは、破棄されるまで引き続きアクセス可能ではありませんか?

class Sample
{
  public:
    int *ptr;
    Sample(int i)
    {
        ptr = new int(i);
    }

    ~Sample()
    {
        delete ptr;
    }
    void PrintVal()
    {
        cout << "The value is " << *ptr;
    }
};

void SomeFunc(Sample x)
{
    cout << "Say i am in someFunc " << endl;
}

int main()
{
    Sample s1 = 10;
    SomeFunc(s1);
    s1.PrintVal(); // dangling pointer
}

ソース

4

4 に答える 4

14

ここでの問題は、の引数に対して行われるコピーSomeFunc()です。そのコピーは、破棄されるとポインターの割り当てを解除します。コピー コンストラクターコピー代入演算子も実装する必要があります。3 のルールを参照してください。

編集:

これは「展開された」疑似コードです。つまり、main()関数でコンパイラが行うことです。

// main
addr0 = grab_stack_space( sizeof( Sample )); // alloc stack space for s1
Sample::ctor( addr0, 10 );                   // call ctor of Sample
addr1 = grab_stack_space( sizeof( Sample )); // alloc stack for argument
Sample::ctor( addr1, addr0 );                // call COPY-ctor of Sample
SomeFunc( addr1 );                           // call SomeFunc
Sample::dtor( addr1 );                       // XXX: destruct the copy
free_stack_space( addr1, sizeof( Sample ));  // free stack taken by copy
Sample::PrintVal( addr0 );                   // call member func on s1
Sample::dtor( addr0 );                       // destruct s1
free_stack_space( addr0, sizeof( Sample ));  // YYY: free stack taken by s1

これは正確な表現ではなく、概念的な説明です。コンパイラがコードに何をしなければならないかという観点から考えるのに役立ちます。

のポインター メンバーはSample、でdeleteマークされたステップで -ed され、ステップ で再び -ed されます。XXXdeleteYYY

于 2010-08-12T14:01:53.980 に答える
3

ニコライの答えはすべてを説明していますが、可能な代替手段は次のとおりです。

の複数のインスタンスがSample同じ基になるポインターを共有する場合boost::shared_ptrは、生のポインターの代わりに次のようなものを使用することもできます。

これには少し費用がかかりますが、おそらく自分でやろうとする場合よりも高くはありません.

さらに、これにより、コピー コンストラクタ、デストラクタ、および代入演算子のいずれかを記述する必要がなくなります。

于 2010-08-12T14:28:07.217 に答える
2

を呼び出すSomeFunc(Sample x)と、オブジェクトはのコピー コンストラクターxを呼び出すことによって作成されます。Sample明示的に記述しなかったため、コンパイラは暗黙的なものを作成します。通常、暗黙的なものは問題ありません。メンバーごとのコピーを行います (コードのvector<int>代わりにa を使用した場合)。int*ただし、この場合はダメです。の値をコピーするだけなptrので、x.ptr と s1.ptr は同じ int[] を指します。これで、SomeFunc が終了しxて破棄され、デストラクタが呼び出されると、意味ptrが削除されます。が使用するメモリを解放しますxが、同じ値なので が使用するメモリでもありますs1

于 2010-08-12T14:09:24.813 に答える
1

ニコライの答えは絶対に正しいです。エレオンのように。

また、値渡しと参照渡しの違いも考慮する必要があります。

SomeFunc が次のように宣言されている場合:

void SomeFunc(Sample& x)

またはさらに良い

void SomeFunc(const Sample& x)

ダングリング ポインターの問題は発生しません。

を定義した方法ではSomeFuncSampleオブジェクトは値渡しされます。つまり、のスコープ内で使用するために一時コピーが作成されますSomeFunc。次に、SomeFunc戻り時に一時オブジェクトがスコープ外になり、そのデストラクタが呼び出され、 が指す整数が削除されptrます。

オブジェクトへの参照を渡すと、Sample呼び出し中にコピーが作成されないため、戻るSomeFuncときにデストラクタが呼び出されません。SomeFunc

于 2010-08-12T14:56:54.400 に答える