在一个相当大的应用程序中,我想跟踪某些类的对象的一些统计信息。为了不降低性能,我希望在拉配置中更新统计数据。因此,我需要在某个位置引用每个活动对象。是否有惯用方式:
我在考虑一组智能指针,但是内存管理会有些颠倒:当智能指针被销毁时,我不想破坏智能指针,而是对象被破坏了。理想情况下,我不想重新发明轮子。
我可以延迟删除指针,我只需要一种方法来快速使它们失效。
编辑:因为稻田要求它:基于拉动的收集的原因是获取信息可能相对昂贵。推动显然是一个干净的解决方案,但被认为太昂贵了。
答案 0 :(得分:2)
该语言没有特殊功能可以让您这样做。有时对象跟踪是通过滚动自己的内存分配器来处理的,但这在堆栈上并不容易。
但是,如果你只使用堆栈,它实际上会让你的问题更容易,假设被跟踪的对象在一个线程上。 C ++对栈的构造和破坏顺序提供了特殊的保证。也就是说,销毁订单正好与施工订单相反。
因此,您可以利用它在每个对象中存储单个指针,以及一个跟踪最新对象的静态指针。现在,您有一个表示为链表的对象堆栈。
template <typename T>
class Trackable
{
public:
Trackable()
: previous( current() )
{
current() = this;
}
~Trackable()
{
current() = previous;
}
// External interface
static const T *head() const { return dynamic_cast<const T*>( current() ); }
const T *next() const { return dynamic_cast<const T*>( previous ); }
private:
static Trackable * & current()
{
static Trackable *ptr = nullptr;
return ptr;
}
Trackable *previous;
}
示例:
struct Foo : Trackable<Foo> {};
struct Bar : Trackable<Bar> {};
// :::
// Walk linked list of Foo objects currently on stack.
for( Foo *foo = Foo::head(); foo; foo = foo->next() )
{
// Do kung foo
}
现在,诚然,这是一个非常简单的解决方案。在大型应用程序中,您可能使用对象有多个堆栈。您可以通过使current()
使用thread_local语义来处理多个线程上的堆栈。虽然你需要一些魔法来使这项工作,因为head()
需要指向一个线程注册表,这需要同步。
您绝对不希望将所有堆栈同步到一个列表中,因为这会破坏您的程序的性能可伸缩性。
至于你的拉动要求,我认为它是一个想要遍历列表的单独线程。您需要一种同步方法,以便在迭代列表时在Trackable<T>
内阻止所有新对象构造或销毁。或类似的。
但至少你可以把这个基本想法扩展到你的需要。
请记住,如果动态分配对象,则无法使用此简单列表方法。为此你需要一个双向列表。
答案 1 :(得分:2)
最简单的方法是在每个对象中包含代码,以便它在实例化时自行注册,并在销毁时自行删除。可以使用CRTP轻松注入此代码:
template <class T>
struct AutoRef {
static auto &all() {
static std::set<T*> theSet;
return theSet;
}
private:
friend T;
AutoRef() { all().insert(static_cast<T*>(this)); }
~AutoRef() { all().erase(static_cast<T*>(this)); }
};
现在,Foo
类可以从AutoRef<Foo>
继承,以便在AutoRef<Foo>::all()
内引用其实例。