存储非拥有引用的对象,必须在引用被破坏之前通知该引用

Bor*_*ein 6 c++ smart-pointers weak-ptr c++11

我有一个遵循这种模式的课程:

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_;
};
Run Code Online (Sandbox Code Playgroud)

这些Foo对象都归Bar:

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

private:
    vector<Foo*> foos_;
};
Run Code Online (Sandbox Code Playgroud)

当然,这是获得想法的简化示例.我保证没有Foos的循环,并且链接的Foos都属于同一个实例Bar.到现在为止还挺好.做事的C++ 11的方式,我会用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
}
Run Code Online (Sandbox Code Playgroud)

我知道可以使用原始指针,通过使用带有后向指针的DAG结构来实现它,因此Foo知道谁"依赖于它们",并且可以在删除之前通知它们(此处此处详述可能的解决方案).

我的问题是:你会以同样的方式处理它吗?有没有办法使用标准的C++ 11智能指针来避免使用显式的反向指针,然后避免areRelativeToMe_[i]->convertToAbsolute();在析构函数中明确地调用Foo?我正在思考weak_ptr,本着以下精神:

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

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

    // ...
}
Run Code Online (Sandbox Code Playgroud)

但问题是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
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

或者喜欢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
    }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

有什么想法吗?

Okt*_*ist 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
}
Run Code Online (Sandbox Code Playgroud)

这将确保在调用其依赖项时过期Foo仍然存在。convertToAbsolute

如何计算children由您决定。我可能会让每个都Foo跟踪自己的子级(循环非拥有指针),但您也可以在内部跟踪它,或者按需Bar搜索以在需要时重新计算它。foos_