C ++使用shared_ptr安全地删除事件对象的负载 [英] C++ safely deleting event object payload with shared_ptr
问题描述
我需要创建一个事件侦听器系统调度的事件
对象。 事件
需要具有以下属性:
I need to create an Event
object to be dispatched by an event listener system. The Event
needs to have following properties:
-
事件
可以由0..n监听器对象处理。 -
事件
包含一个void指针,指向任意对象(有效载荷)(构建时为未知类型)。事件侦听器将根据事件
的名称转换为适当的类型。 - 需要有效载荷对象(自动)删除一次事件已派到有关方面。
- 假设在事件处理时,侦听器可以制作浅色的有效载荷副本。
Event
can potentially be handled by 0..n listener objects.Event
contains a void pointer which can point to an arbitrary object (payload) (unknown type at build time). Event listener will convert to appropriate type depending onEvent
's name.- Need the payload object to be (automatically) deleted once the event has been dispatched to interested parties. Original event raiser cannot deallocate as event goes into an asvnc queue.
- Assume listeners can make shallow copy of payload when event is processed.
我已经实施了解决方案这里,但是AFAIK这导致有效载荷被释放(通过 unique_ptr
)在第一个事件处理程序之后。
I have implemented the solution here, but AFAIK this causes the payload to be deallocated (via the unique_ptr
) after the first event handler.
在下面的代码中,'setData'尝试有效载荷对象( dataObject
),并将其转换为 shared_ptr
,由 void * data
。 getData
执行reverse:
In the code below, 'setData' attempts to take the payload object (dataObject
), and to convert it into a shared_ptr
to be carried by void* data
. getData
does the "reverse":
class Event {
public:
std::string name;
Event(std::string n = "Unknown", void* d = nullptr) :name(n), data(d) {}
template<class T> void setData(const T dataObject)
{
//Create a new object to store the data, pointed to by smart pointer
std::shared_ptr<T> object_ptr(new T);
//clone the data into the new object
*object_ptr = dataObject;
//data will point to the shared_pointer
data= new std::shared_ptr<T>(object_ptr);
}
//reverse of setData.
template<class T> T getData() const
{
std::unique_ptr<
std::shared_ptr<T>
> data_ptr((std::shared_ptr<T>*) data);
std::shared_ptr<T> object_ptr = *data_ptr;
return *object_ptr;
}
private:
void* data;
};
推荐答案
你应该考虑 std :: any
而不是无效*
。这将避免数据
的复杂内存分配。如果您不能使用C ++ 17,从 Kevlin Henney的文章(添加C ++ 17规范中缺少的部分,例如移动构造函数)。
You should consider std::any
instead of void*
. That would avoid complex memory allocation for data
. If you can't use C++17, it's not that hard to make your own implementation from Kevlin Henney's paper (add parts missing from C++17 specification, e.g. move constructor).
您的代码可能会成为某事像这样:
Your code may become something like that:
class Event {
public:
std::string name;
Event() :name("Unknown") {}
template<class T>
Event(std::string n, T&& dataObject) :name(n)
{
setData(std::forward<T>(dataObject));
}
template<class T> void setData(T&& dataObject)
{
using data_t = typename std::decay<T>::type;
data = std::make_shared<data_t>(std::forward<T>(dataObject));
}
//reverse of setData.
template<class T> T getData() const
{
using data_t = typename std::decay<T>::type;
return *any_cast<std::shared<data_t>>(data);
}
private:
any data;
};
我在代码中使用了lvalue引用的模板避免重载的方法:模板扣除允许使用相同的方法接受命名变量以及具有或不具有常量的临时值。有关详细信息,请参阅此处。
std :: forward 用于执行完全转发。实际上,如果你从这样的左值构造一个事件
:
std::forward is used to perform perfect forwarding. Indeed, if you construct an Event
from an lvalue like this:
Event e{"Hello", Foo{}};
调用 setData
没有完美的转发将通过 dataObject
作为一个左值,因为它是这个上下文中的一个命名变量:
Calling setData
without perfect forwarding will pass dataObject
as an lvalue since it is a named variable in this context:
setData(dataObject); // will call Foo's copy constructor
完美转发将通过 dataObject
作为一个右值,但只有当是从一个rvalue构造的时候才是:
Perfect forwarding will pass dataObject
as an rvalue, but only if it was constructed from an rvalue in the first place:
setData(std::forward<T>(dataObject)); // will call Foo's move constructor
如果 dataObject
从左值构造,相同的 std :: forward
将会作为左值传递,并根据需要生成复制构造函数调用:
If dataObject
had been constructed from an lvalue, the same std::forward
will pass it as an lvalue, and yield copy constructor invokation, as wanted:
Foo f{};
Event e{"Hello", f};
// ...
setData(std::forward<T>(dataObject)); // will call Foo's copy constructor
如果您想继续使用指向 void
的指针,则可以使用 shared_ptr
嵌入一个具有约束力的删除器:/ p>
If you want to keep using pointers to void
, you can embed an appriopriate deleter with the shared_ptr
:
template<class T> void setData(T&& dataObject)
{
using data_t = typename std::decay<T>::type;
data = std::shared_ptr<void>(
new data_t{std::forward<T>(dataObject)},
[](void* ptr)
{
delete static_cast<data_t*>(ptr);
}
);
}
使用数据
声明为 shared_ptr< void>
和 getData
:
template<class T> T getData() const
{
using data_t = typename std::decay<T>::type;
return *std::static_pointer_cast<data_t>(data);
}
这篇关于C ++使用shared_ptr安全地删除事件对象的负载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!