6

私はこのパターンに従うクラスを持っています:

class Foo
{
public:
    // Create a Foo whose value is absolute
    Foo(int x) : other_(0), a_(x)  {}

    // Create a Foo whose value is relative to another Foo
    Foo(Foo * other, int dx) : other_(other), a_(dx) {}

    // Get the value
    double x() const
    {
        if(other_)
            return other_->x() + a_;
        else
            return a_;
    }

private:
    Foo * other_;
    int a_;
};

Fooオブジェクトはすべて によって所有されていますBar:

class Bar
{
public:
    ~Bar() { for(int i=0; i<foos_.size(); i++) delete foos_[i]; }

private:
    vector<Foo*> foos_;
};

もちろん、これはアイデアを得るための単純化された例です。Foos のループがないこと、およびリンクされFooた s がすべて の同じインスタンスに属していることを保証しますBar。ここまでは順調ですね。C++11 のやり方で物事を行うには、 in を使用vector< unique_ptr<Foo> > foos_;し、コンストラクターの潜在的な引数としてBar渡します。foos_[i].get()Foo

契約があります:

これは GUI アプリケーションであり、ユーザーはインタラクティブに一部Fooを自由に削除できます。予想される動作は、foo1が削除され、foo2に対して相対的である場合、 「絶対」foo1になることです。foo2

void Foo::convertToAbsolute() { a_ += other_->x(); other_ = 0; }

void usageScenario()
{
    Foo * foo1 = new Foo(42);      
    Foo * foo2 = new Foo(foo1, 42);
    // Here, foo1->x() = 42 and foo2->x() = 84

    foo1->setX(10);
    // Here, foo1->x() = 10 and foo2->x() = 52

    delete foo1;
    // Here, foo2->x() = 52
}

バックポインターを含む DAG 構造を使用することにより、生のポインターを使用してそれを行うことができることを知っているので、Foo誰が「それらに依存している」かを認識し、削除する前にそれらを通知できます (可能な解決策については、こちらこちらで詳しく説明しています)。

私の質問は次のとおりです。同じように処理しますか? 標準の C++11 スマート ポインターを使用して、明示的なバックポインターを回避areRelativeToMe_[i]->convertToAbsolute();し、デストラクタで明示的に呼び出すことを回避する方法はありFooますか? 私は次のようなことを考えてweak_ptrいました:

class Foo { /* ... */ weak_ptr<Foo> other_; };

double Foo::x() const
{
    if(other_.isExpired())
        convertToAbsolute();

    // ...
}

しかし、問題はconvertToAbsolute()、相対Fooがまだ存在する必要があることです。したがって、「この参照は論理的に期限切れです」と伝えることができる非所有のスマートポインターが必要ですが、実際には、参照されたオブジェクトが不要になるまで、その有効期間を延長します。

weak_ptr他のものと共有されなくなるまで、有効期間を延長するように見える可能性がありますweak_ptr

class Foo { /* ... */ extended_weak_ptr<Foo> other_; };

double Foo::x() const
{
    if(other_.isExpired())
    {
        convertToAbsolute();
        other_.reset(); // now the object is destructed,  unless other
                          // foos still have to release it
    }

    // ...
}

またはshared_ptr、所有権のレベルが異なる のように:

class Bar { /* ... */ vector< multilevel_shared_ptr<Foo> foos_; };

class Foo { /* ... */ multilevel_shared_ptr<Foo> other_; };

void Bar::createFoos()
{ 
    // Bar owns the Foo* with the highest level of ownership "Level1"

    // Creating an absolute Foo
    foos_.push_back( multilevel_unique_ptr<Foo>(new Foo(42), Level1) );

    // Creating a relative Foo 
    foos_.push_back( multilevel_unique_ptr<Foo>(new Foo(foos_[0],7), Level1) );
}

Foo::Foo(const multilevel_unique_ptr<Foo> & other, int dx) :
    other_( other, Level2 ),
   // Foo owns the Foo* with the lowest level of ownership "Level2"
    a_(dx) 
{
}

double Foo::x() const
{
    if(other_.noLevel1Owner()) // returns true if not shared 
                               // with any Level1 owner
    {
        convertToAbsolute();
        other_.reset(); // now the object is destructed, unless 
                        // shared with other Level2 owners
    }
    // ...
}

何かご意見は?

4

5 に答える 5

1

すべてFooが所有していBarます。したがって、すべての削除はメソッドでFoo発生しBarます。したがって、このロジックを内部に実装する可能性がありますBar

void Bar::remove(Foo* f)
{
    using namespace std::placeholders;
    assert(std::any_of(begin(foos_), end(foos_),
                       std::bind(std::equal_to<decltype(f)>(), f, _1));

    auto const& children = /* some code which determines which other Foo depend on f */;
    std::for_each(begin(children), end(children),
                  std::mem_fn(&Foo::convertToAbsolute));
    foos_.remove(f);

    delete f; // not needed if using smart ptrs
}

これにより、依存関係で が呼び出されたFooときに、有効期限が切れたままであることを保証できます。convertToAbsolute

計算方法の選択はchildrenあなた次第です。おそらく、それぞれFooに独自の子 (循環的な非所有ポインター) を追跡させますが、内部Barで追跡することも、必要に応じてオンデマンドで検索しfoos_て再計算することもできます。

于 2013-09-13T21:26:36.047 に答える
1

Signal/Slot フレームワークを使用している場合は、リンクを解除するのに適した場所が提供されます。たとえば、Qt ライブラリを使用すると、これらのクラスは次のようになります。

class Foo : public QObject
{
Q_OBJECT
public:
    // Create a Foo whose value is absolute
    Foo(int x) : QObject(nullptr), other_(nullptr), a_(x) {}

    // Create a Foo whose value is relative to another Foo
    Foo(Foo * other, int dx) : QObject(nullptr) other_(other), a_(dx) {
        connect(other, SIGNAL(foo_dying()), this, SLOT(make_absolute()));
    }

    ~Foo() { emit foo_dying(); }

    // Get the value
    double x() const
    {
        if(other_)
            return other_->x() + a_;
        else
            return a_;
    }

signals:
    void foo_dying();

private slots:
    void make_absolute()
    {
        a_ += other_->x();
        other_ = nullptr;
    }

private:
    Foo * other_;
    int a_;
};
于 2013-09-13T22:13:42.090 に答える