知道何时从 std::map<void *, ...中删除关联的用户数据>
Knowing when to delete associated user data from a std::map<void *, ...>
我有一个地址映射,允许我用对象存储任意数据。基本上,我正在编写的库具有一个模板化函数,该函数最终使用对象存储任意数据。
std::map<void *, MyUserData>
可以工作,直到传入的对象被销毁,将其用户数据留在映射中。我希望关联的用户数据也被删除,所以我需要以某种方式监听传入对象
的析构函数。说明问题的一些示例代码:
#include <map>
#include <memory>
struct MyUserData
{
int someNum;
};
std::map<void *, MyUserData> myMap;
template <typename T>
registerObject<T>(const std::shared_ptr<T> & _object)
{
static inc = 0;
myMap[(void *)&_object->get()].someNum = inc++;
}
struct MyObject
{
int asdf;
};
int main(int _argc, char ** _argv)
{
auto obj = std::make_shared<MyObject>();
obj->asdf = 5;
registerObject(obj);
obj = 0;
//The user data is still there. I want it to be removed at this point.
}
我目前的解决方案是在shared_ptr上设置一个自定义删除器。当对象的析构函数被调用时,这将向我发出信号,并告诉我何时删除相关的用户数据。不幸的是,这需要我的库创建shared_ptr,因为没有"set_deleter"函数。必须在构造函数中初始化。
mylib::make_shared<T>(); //Annoying!
我也可以让用户手动删除他们的对象:
mylib::unregister<T>(); //Equally annoying!
我的目标是能够在没有任何预先注册的情况下惰性地添加对象。
总的来说,我想检测对象何时被删除,并知道何时从std::map
中删除对应的对象。
有什么建议吗?
注:我是否应该担心将用户数据留在地图中?对象被分配与先前删除的对象相同地址的可能性有多大?(就我的lib而言,它最终会接收相同的用户数据。)
编辑:我认为我一开始没有很好地表达我的问题。重写。从您的代码示例中,看起来外部接口是
template <typename T>
registerObject<T>(const std::shared_ptr<T> & _object);
我假设在某处有一个get风格的API。我们称它为getRegisteredData
。(可能是内部的)
在这个问题的范围内,我将使用std::weak_ptr<void>
而不是void*
,因为std::weak_ptr<T>
可以判断何时不再有对周围对象的"强引用",但不会通过维护引用来阻止对象被删除。
std::map<std::weak_ptr<void>, MyUserData> myMap;
template <typename T>
registerObject<T>(const std::shared_ptr<T> & _object)
{
static inc = 0;
Internal_RemoveDeadObjects();
myMap[std::weak_ptr<void>(_object)].someNum = inc++;
}
template <typename T>
MyUserData getRegisteredData(const std::shared_ptr<T> & _object)
{
Internal_RemoveDeadObjects();
return myMap[std::weak_ptr<void>(_object)];
}
void Internal_RemoveDeadObjects()
{
auto iter = myMap.cbegin();
while (iter != myMap.cend())
{
auto& weakPtr = (*iter).first;
const bool needsRemoval = !(weakPtr.expired());
if (needsRemoval)
{
auto itemToRemove = iter;
++iter;
myMap.erase(itemToRemove);
}
else
{
++iter;
}
}
}
基本上,std::weak_ptr
和std::shared_ptr
协作,std::weak_ptr
可以检测到何时没有更多的std::shared_ptr
引用到相关对象。在这种情况下,我们可以从myMap
中删除辅助数据。我使用myMap
的两个接口,您的registerObject
和我的getRegisteredData
作为调用Internal_RemoveDeadObjects
执行清理的方便位置。
是的,每次注册一个新对象或请求注册的数据时,这将遍历整个myMap
。如果你觉得合适,可以修改,或者尝试不同的设计
你问"我是否应该担心将用户数据留在地图中?"对象被分配的地址与先前删除的对象相同的可能性有多大?"根据我的经验,绝对不是零,所以不要这样做。: -)
我将添加一个注销方法,并让用户注销他们的对象。使用给定的接口,在剥离类型的地方,我看不到检查reff计数的方法,并且c++没有提供检查内存是否已删除的方法。
我想了一会儿,这是我得到的:
#include <memory>
#include <map>
#include <iostream>
#include <cassert>
using namespace std;
struct MyUserData
{
int someNum;
};
map<void *, MyUserData> myMap;
template<class T>
class my_shared_ptr : public shared_ptr<T>
{
public:
my_shared_ptr() { }
my_shared_ptr(const shared_ptr<T>& s) : shared_ptr<T>(s) { }
my_shared_ptr(T* t) : shared_ptr<T>(t) { }
~my_shared_ptr()
{
if (unique())
{
myMap.erase(get());
}
}
};
template <typename T>
void registerObject(const my_shared_ptr<T> & _object)
{
static int inc = 0;
myMap[(void *)_object.get()].someNum = inc++;
}
struct MyObject
{
int asdf;
};
int main()
{
{
my_shared_ptr<MyObject> obj2;
{
my_shared_ptr<MyObject> obj = make_shared<MyObject>();
obj->asdf = 5;
registerObject(obj);
obj2 = obj;
assert(myMap.size() == 1);
}
/* obj is destroyed, but obj2 still points to the data */
assert(myMap.size() == 1);
}
/* obj2 is destroyed, nobody points to the data */
assert(myMap.size() == 0);
}
但是请注意,如果您编写obj = nullptr;
或obj.reset()
,它将不起作用,因为在这些情况下对象不会被销毁(没有调用析构函数)。另外,你不能在这个解决方案中使用auto。
另外,要注意不要像刚才那样调用(void *)&_object.get()。如果我没有大错特错的话,那句话实际上是在获取_object.get()返回的临时对象的地址,并将其强制转换为void。
这听起来像是…boost::intrusive
(http://www.boost.org/doc/libs/1_53_0/doc/html/intrusive.html) !我不认为目前的界面会完全像现在这样工作。当我有机会的时候,我会试着解决更多的细节。
你可以直接做
map.erase(map.find(obj));
delete obj;
obj = 0;
这将调用用户数据的析构函数,并将其从映射中删除。
或者你可以创建自己的经理:
class Pointer;
extern std::map<Pointer,UserData> data;
class Pointer
{
private:
void * pointer;
public:
//operator ()
void * operator()()
{
return pointer;
}
//operator =
Pointer& operator= (void * ptr)
{
if(ptr == 0)
{
data.erase(data.find(pointer));
pointer = 0;
}
else
pointer = ptr;
return *this;
}
Pointer(void * ptr)
{
pointer = ptr;
}
Pointer()
{
pointer = 0;
}
~Pointer(){}
};
struct UserData
{
static int whatever;
UserData(){}
};
std::map<Pointer,UserData> data;
int main()
{
data[Pointer(new UserData())].whatever++;
data[Pointer(new UserData())].whatever++;
data[Pointer(new UserData())].whatever++;
data[Pointer(new UserData())].whatever++;
Pointer x(new UserData());
data[x].whatever;
x = 0;
return 0;
}
- 将数组的地址分配给变量并删除
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- C/C++编译器通常会删除重复的库吗
- 从链接列表c++中删除一个项目
- C++如何通过用户输入删除列表元素
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 是否需要删除包含对象的"pair"?
- 如何在自删除后将对象设置为nullptr
- 迭代时从向量和内存中删除对象
- 使用函数"remove"删除重复元素
- 如何从多映射中删除特定的重复项
- 运算符C++ "delete []"仅删除 2 个前值
- 删除指向指针的指针是运行时错误吗
- 将指针设置为"nullptr"并不能防止双重删除?
- 为什么示例代码访问IUnknown中已删除的内存
- 如何通过 getter 函数删除矢量的元素?
- 从控制台中删除最后打印的元素
- C++中的线程安全删除
- 如何从存储在std::映射中的std::集中删除元素
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数