我有一个遵循这种模式的课程:
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_;
};
当然,这是一个简单的例子来获得这个想法。我保证没有Foo
s的循环,并且链接的Foo
都属于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
}
我知道可以使用原始指针,使用带有反向指针的DAG结构,因此Foo
知道“依赖于它们”的人,并且可以在删除之前通知它们(可能详细解决方案here和here)。
我的问题是:你会以同样的方式处理吗?有没有办法使用标准的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
}
// ...
}
有什么想法吗?
答案 0 :(得分:1)
即使有多个其他依赖对象,也可以使用双链接方法。您只需将同一对象的依赖项链接在一起:
class Foo {
public:
explicit Foo(double x)
: v(x), foot(nullptr), next(nullptr), dept(nullptr) {}
// construct as relative object; complexity O(1)
Foo(Foo*f, double x)
: v(x), foot(f), dept(nullptr)
{ foot->add_dept(this); }
// destruct; complexity O(n_dept) + O(foot->n_dept)
// O(1) if !destroy_carefully
~Foo()
{
if(destroy_carefully) {
for(Foo*p=dept; p;) {
Foo*n=p->next;
p->unroot();
p=n;
}
if(foot) foot->remove_dept(this);
}
}
double x() const
{ return foot? foot->x() + v : v; }
private:
double v; // my position relative to foot if non-null
Foo*foot; // my foot point
Foo*next; // next object with same foot point as me
Foo*dept; // first object with me as foot point
// change to un-rooted; complexity: O(1)
void unroot()
{ v+=foot->x(); foot=nullptr; next=nullptr; }
// add d to the linked list of dependents; complexity O(1)
void add_dept(const Foo*d)
{ d->next=dept; dept=d; }
// remove d from the linked list of dependents ; complexity O(n_dept)
void remove_dept(const Foo*d)
{
for(Foo*p=dept; p; p=p->next)
if(p==d) { p=d->next; break; }
}
static bool destroy_carefully;
};
bool Foo::destroy_carefully = true;
在这里,设置Foo::destroy_carefully=false
允许您删除所有剩余的对象,而无需进行相互引用的解开(这可能很昂贵)。
答案 1 :(得分:1)
有趣的问题。我想你可以想到你可以添加一个指向'child'对象的指针。我不确定,智能指针是否有帮助。我尝试使用std::weak_ptr<Foo>
实现下面的代码,但您只能将其用于other_
,而不能用于侦听器。
我的另一个想法是将责任留给更高的权力。您遇到的问题是您希望在调用析构函数时执行更新。也许更好的方法是从其他地方调用convertToAbsolute()
。例如,如果要将Foos
存储在向量中并且用户在UI中单击“删除”,则需要删除对象的索引,以便将相邻项更新为绝对值。
以下是使用Foo*
。
#include <iostream>
#include <memory>
#include <vector>
class Foo
{
public:
// Create a Foo whose value is absolute
Foo(int x) : other_(nullptr), listener_(nullptr), a_(x)
{}
// Create a Foo whose value is relative to another Foo
Foo(Foo* other, int dx) :
other_(other), listener_(nullptr), a_(dx)
{
other->setListener(this);
}
~Foo()
{
convertToAbsolute();
if (listener_)
listener_->other_ = nullptr;
}
// Get the value
double x() const
{
if(other_)
return other_->x() + a_;
else
return a_;
}
void setX(int i)
{
a_ = i;
}
void convertToAbsolute()
{
if (listener_)
listener_->a_ += a_;
}
void setListener(Foo* listener)
{
listener_ = listener;
}
private:
Foo* other_;
Foo* listener_;
int a_;
};
void printFoos(const std::vector<std::shared_ptr<Foo>>& foos)
{
std::cout << "Printing foos:\n";
for(const auto& f : foos)
std::cout << '\t' << f->x() << '\n';
}
int main(int argc, const char** argv)
{
std::vector<std::shared_ptr<Foo>> foos;
try
{
auto foo1 = std::make_shared<Foo>(42);
auto foo2 = std::make_shared<Foo>(foo1.get(), 42);
foos.emplace_back(foo1);
foos.emplace_back(foo2);
}
catch (std::exception& e)
{
std::cerr << e.what() << '\n';
}
// Here, foo1->x() = 42 and foo2->x() = 84
printFoos(foos);
foos[0]->setX(10);
// Here, foo1->x() = 10 and foo2->x() = 52
printFoos(foos);
foos.erase(foos.begin());
// Here, foo2->x() = 52
printFoos(foos);
return 0;
}
答案 2 :(得分: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_
在需要时重新计算它。
答案 3 :(得分: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_;
};
答案 4 :(得分:0)
这可能是使用反向指针实现目标的最简单方法。您可以根据复杂性要求使用所需的容器(例如,集合,哈希表,向量,链表等)。 proposed by Walter是一种更复杂但更有效的方法。
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)
{
other->areRelativeToMe_.insert(this);
}
// Get the value
double x() const
{
if(other_)
return other_->x() + a_;
else
return a_;
}
// delete the Foo
Foo::~Foo()
{
// Inform the one I depend on, if any, that I'm getting destroyed
if(other_)
other_->areRelativeToMe_.remove(this);
// Inform people that depends on me that I'm getting destructed
for(int i=0; i<areRelativeToMe_.size(); i++)
areRelativeToMe_[i]->convertToAbsolute();
}
private:
Foo * other_;
int a_;
Container<Foo*> areRelativeToMe_; // must provide insert(Foo*)
// and remove(Foo*)
// Convert to absolute
void convertToAbsolute()
{
a_ += other_->x();
other_ = 0;
}
};