3

私は C++ が初めてで、これまでに学んだことから、ヒープ上に作成されたものを指すポインターで delete を呼び出すと、そのポインターが指すものはすべて消去され、メモリが解放されますよね?

ただし、単純なクラスでこれを試したとき:

class MyClass
{
    int _Id;
public:
    MyClass(int id) : _Id(id)
    {
        std::cout << "$Constructing the damn thing! " << _Id << std::endl;
    }
    ~MyClass()
    {
        std::cout << "?Destructing the damn thing! " << _Id << std::endl;
    }
    void Go_XXX_Your_Self()
    {
        std::cout << "%OooooooooO NOOOOOO! " << _Id << std::endl;
        delete this;
    }
    void Identify_Your_Self()
    {
        std::cout << "#Object number: " << _Id << " Located at: " << this << std::endl;
    }
};

これらは、削除がどのように機能するかを確認するための愚かなテストです。

int main()
{
    MyClass* MC1 = new MyClass(100);
    MyClass* MC2 = new MyClass(200);
    MyClass* MC3 = MC2;

    std::cout << MC1 << " " << MC2 << " " << MC3 << " " << std::endl;

    MC1->Identify_Your_Self();
    MC2->Identify_Your_Self();
    MC3->Identify_Your_Self();

    delete MC1;

    MC1->Identify_Your_Self();


    MC3->Go_XXX_Your_Self();

    MC3->Identify_Your_Self();


    delete MC2;

    MC2->Identify_Your_Self();

    MC2->Go_XXX_Your_Self();

    MC2->Identify_Your_Self();

    return 0;
}

出力は次のとおりです。

$Constructing the damn thing! 100
$Constructing the damn thing! 200
0x3e3e90 0x3e3eb0 0x3e3eb0
#Object number: 100 Located at: 0x3e3e90
#Object number: 200 Located at: 0x3e3eb0
#Object number: 200 Located at: 0x3e3eb0
?Destructing the damn thing! 100
#Object number: 0 Located at: 0x3e3e90
%OooooooooO NOOOOOO! 200
?Destructing the damn thing! 200
#Object number: 4079248 Located at: 0x3e3eb0
?Destructing the damn thing! 4079248
#Object number: 4079280 Located at: 0x3e3eb0
%OooooooooO NOOOOOO! 4079280
?Destructing the damn thing! 4079280
#Object number: 4079280 Located at: 0x3e3eb0

それで、私の質問は、オブジェクトが削除された後でも Go_XXX_Your_Self() と Identify_Your_Self() を呼び出すことができるのはなぜですか?

これはC ++でどのように機能しますか?(削除しても残っていますか?)

また、無いか確認して頂けないでしょうか?(理論的には不可能であることはわかっていますが、どのような方法があるのか​​ 知りたいです)

4

5 に答える 5

9

それで、私の質問は、オブジェクトが削除された後でも Go_XXX_Your_Self() と Identify_Your_Self() を呼び出すことができるのはなぜですか?

未定義の動作のため。

これはC ++でどのように機能しますか?(削除しても残っていますか?)

未定義の動作のため。他の実装でも同じように動作するという保証はありません。繰り返しますが、未定義の動作です。

また、無いか確認して頂けないでしょうか?(理論的には不可能であることはわかっていますが、どのような方法があるのか​​ 知りたいです)

delete MC1;
MC1 = nullptr;

ポインターをingnullptr後に に設定deleteすることにより、ランタイムは、ユーザーが無効で使用する権利のない場所にアクセスしていることを検出する可能性が最も高くなります。また、適用可能なすべてのポインターに対してこれを入念に行うことにより、オブジェクトが有効かどうか (非の場合に有効nullptr) を確認できます。

if(my_ptr) {
   // my_ptr is most possibly valid (though you can still go wrong)
   // use my_ptr
}

同様に、未加工のポインタnullptrがまだ有効なアドレスに初期化されていない場合にも設定する必要があります。

MyClass* some_ptr = nullptr;
...

ただし、最新の C++11 機能にアクセスできる場合は、生のポインターをまったく使用せず、std::unique_ptrorのみを使用する方がはるかに優れていstd::shared_ptrます (必要なセマンティクスによって異なります)。また、将来の C++ 標準リビジョンでは、非所有で監視のみのポインター ラッパーである提案を使用することもできます。std::exempt_ptr

于 2013-08-28T02:08:21.717 に答える
2

オブジェクトを呼び出すdeleteと、そのオブジェクトによって使用されるメモリは、別の用途new(または、実際にはヒープを使用するもの) によって使用できるようになります。その時まで、削除されたオブジェクトは以前の値を保持する場合があります (保持しない場合もあります)。しかし、最終的には、プログラムの実行が進むにつれて、削除されたオブジェクトが使用していたメモリが上書きされ、運が良ければ悪いことが起こります。プログラムがクラッシュする可能性があります。運が悪ければ、現場に展開されるまでプログラムがクラッシュすることはありません。

于 2013-08-28T02:12:22.623 に答える
0

メカニズムは、delete は、メモリが不要になり、再利用できることをシステムに伝えるだけです。他のアクションは実行されません。これは、セキュリティ開示で見られる解放後の使用エラーの基礎です。メモリを再び使用しないことは、プログラマ次第です。

そのため、RAIIはこの問題の発生を減らすための 1 つの方法です。

于 2013-08-28T03:19:51.513 に答える
0

あなたは不運でした。メモリはまだ上書きされておらず、物事はまだ正常に機能していました.

別の機会に、運が良ければ激しくクラッシュし、問題が浮き彫りになるかもしれません。

行ったことのあるメモリにアクセスすることdeleteは、決してすべきことではありません。C++ では、コンパイラが停止しないため、そうしないのはあなた次第です。

于 2013-08-28T02:05:00.367 に答える
0

通常、C++ コンパイラは、ユーザーに代わってポインターの有効性をチェックするコードを生成しません。気にする場合は、自分でコードを提供する必要があります。解放されたメモリにアクセスすると未定義の動作が発生するため、その結果は非決定論的です。

いずれにせよ生のポインターを操作するべきではなく、代わりにスマート ポインターの 1 つに依存する必要があります。そうすれば、ポインターがスコープから外れてメモリが破壊されることに依存するため、このエラーが発生する可能性ははるかに低くなります。

ただし、それでも、次のような悪いことを強制するケースに遭遇する可能性があります。

    std::unique_ptr<Foo> p(new Foo);
    p->foo();
    p.reset(0);
    p->foo();

何らかの形式のチェックを行うスマート ポインター ラッパーを使用して、実行時にこのような問題をキャッチすることができます。

template <typename T>
class CheckedSharedPtr {
    std::shared_ptr<T> ptr_;
public:
    //...
    T * operator -> () const {
        if (ptr_ == 0) {
            //...throw something
        }
        return ptr_.operator->();
    }
};

ただし、これはすべてのポインター アクセスに余分なオーバーヘッドを追加するだけです。より良い方法は、コード内のこの種の問題や他の多くの問題を特定するのに役立つ優れた静的コード分析ツールを利用することです。

于 2013-08-28T02:35:47.633 に答える