5

従来のポインタを使用して、でdelete this何かを割り当てたときはいつでも、C++で言うことができることを私は知っています。new実際、慎重に扱うのが良い習慣であることも知っています。delete thisオブジェクトが保持されているかどうかをオブジェクトに言わせることはできますstd::shared_ptrか?そして、それはデストラクタと呼ばれるべきですよね?アイデアを出すために、船がミサイルを発射できるゲームを作っています。ミサイルを自分で削除してもらいたいのです。

4

4 に答える 4

13

いいえ、安全ではありません。オブジェクトの寿命は の所有者によって決定されるshared_ptrため、オブジェクト自体は、死にたいかどうかを判断できません。そうすれば、最後のshared_ptr死亡時に二重削除されます。私が提供できる唯一の解決策は、「設計を再考する」ことです (おそらくそもそも必要ないでしょうしshared_ptr、ミサイルはおそらく値またはプールされたオブジェクトである可能性があります)。

于 2012-04-22T19:16:50.020 に答える
3

ミサイルが自分自身を削除するには、自分自身を所有するか、少なくとも自分自身の所有権を他のユーザーと共有する必要があります。ミサイルには があるとおっしゃっているのでshared_ptr、ミサイルの所有権を共有する複数のオブジェクトが既にあると仮定しています。

ミサイルがshared_ptr自分自身に a を保持し、自分自身の所有権を共有することは可能です。ただし、これは常に周期的な所有パターンを作成します。ミサイルの shared_ptr データ メンバーがそれ自体を参照している限り、参照カウントがゼロになることはありません。そのため、ミサイルはリークされます。

外部オブジェクトまたはイベントでミサイルにそれ自体を削除するように指示することもできますが、その場合、何がポイントなのかわかりません。ミサイルに自分自身を削除するように指示するために、その通信は を介し​​て行われる必要があり、ミサイルshared_ptrを手放すまで削除は実際には行われませんshared_ptr

はい、可能です。いいえ、それは良い考えではないと思います。私にはメモリリークが発生しやすいように見え、実際には価値がありません。しかし、好奇心旺盛な人のために、これを行う方法は次のとおりです。

#include <iostream>
#include <memory>

class missile
    : public std::enable_shared_from_this<missile>
{
    std::shared_ptr<missile> self_;
public:
    missile()
      {} 

    ~missile() {std::cout << "~missile()\n";}

    void set_yourself()
    {
        self_ = shared_from_this();
    }
    void delete_yourself()
    {
        if (self_)
            self_.reset();
    }
};

int main()
{
    try
    {
        std::shared_ptr<missile> m = std::make_shared<missile>();
        m->set_yourself();
        std::weak_ptr<missile> wp = m;
        std::cout << "before first reset()\n";
        m.reset();
        std::cout << "after first reset()\n";
        // missile leaked here
        m = wp.lock();
        m->delete_yourself();
        std::cout << "before second reset()\n";
        m.reset();  // missile deleted here
        std::cout << "after second reset()\n";
    }
    catch (const std::exception& e)
    {
        std::cout << e.what() << '\n';
    }
}
于 2012-04-22T20:45:28.107 に答える
1

この質問はかなり古いものですが、同様の問題がありました (この場合、ウィーク ポインターを共有しながら独自のライフサイクルを管理する必要がある「リスナー」オブジェクト)。次のことを前提として、見つけた解決策を共有しています。

  • オブジェクトはそれ自身のライフサイクルを管理するため、share_ptr を共有することはありませんが、weak_ptr を共有します (shared_ptr の同様のソリューション + use_shared_from_this が必要な場合)。
  • RAII を壊すのは悪い考えなので、私たちはそれをしません: ここで対処するのは、オブジェクト自体が shared_ptr を所有するという問題です。デストラクタが 2 回呼び出されるため、クラッシュ (または少なくとも未定義の動作) が発生します (1 回目は通常のオブジェクトの破棄時、2 回目は自己完結型の shared_ptr メンバーの破棄時)。

コード:

#include <memory>
#include <stdio.h>

using std::shared_ptr;
using std::weak_ptr;

class A {
    struct D {
            bool deleted = false;
            void operator()(A *p) {
                printf("[deleter (%s)]\n", p, deleted ? "ignored":"deleted");
                if(!deleted) delete p;
        }
    };

    public: shared_ptr<A> $ptr = shared_ptr<A>(this, D());

    public: ~A() {
        std::get_deleter<A::D>($ptr)->deleted = true;
    }

    public: weak_ptr<A> ptr() { return $ptr; }
};

void test() {
    A a;

    printf("count: %d\n", a.ptr().lock().use_count());
    printf("count: %d\n", a.ptr().use_count());
}

int main(int argc, char *argv[]) {
    puts("+++ main");

    test();

    puts("--- main");
}

出力:

$ g++ -std=c++11 -o test test.cpp && ./test
+++ main
count: 2
count: 1
[deleter (ignored)]
--- main

shared_ptr デリータは、スタックに割り当てられたオブジェクトに対して呼び出されることはありません。そのため、通常のオブジェクトの破棄時に呼び出されると、単に削除をバイパスします (デフォルトのオブジェクト デストラクタが既に呼び出されているため、この時点に到達しました)。

于 2015-09-15T10:53:04.940 に答える