gpt4 book ai didi

C++ 使用 shared_ptr 安全删除事件对象负载

转载 作者:搜寻专家 更新时间:2023-10-31 02:13:47 24 4
gpt4 key购买 nike

我需要创建一个 Event 对象以由事件监听器系统分派(dispatch)。 Event 需要具有以下属性:

  1. Event 可能由 0..n 个监听器对象处理。
  2. Event 包含一个 void 指针,它可以指向任意对象(有效负载)(构建时未知类型)。事件监听器将根据 Event 的名称转换为适当的类型。
  3. 需要在事件发送给相关方后(自动)删除有效负载对象。当事件进入 asvnc 队列时,原始事件引发者无法解除分配。
  4. 假设监听器可以在处理事件时对负载进行浅拷贝。

我已经实现了解决方案 here ,但是据我所知,这会导致负载在第一个事件处理程序之后被释放(通过 unique_ptr)。

在下面的代码中,'setData' 尝试获取负载对象 (dataObject),并将其转换为 shared_ptr 以由 void 携带*数据getData 执行“反向”操作:

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 而不是 void* .这将避免复杂的内存分配 data .如果你不能使用 C++17,那么从 Kevlin Henney's paper 中创建你自己的实现并不难(添加 C++17 规范中缺少的部分,例如移动构造函数)。

你的代码可能会变成这样:

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;
};

我在代码中使用了模板方法中的左值引用来避免重载:模板推导允许相同的方法接受命名变量和临时值,无论是否具有常量性。参见 here了解详情。

std::forward用于执行 perfect forwarding .事实上,如果你构造一个 Event来自这样的左值:

Event e{"Hello", Foo{}};

调用 setData不完美转发将通过dataObject作为左值,因为它在此上下文中是命名变量:

setData(dataObject); // will call Foo's copy constructor

完美转发会通过dataObject作为右值,但仅当它首先是从右值构造的:

setData(std::forward<T>(dataObject)); // will call Foo's move constructor

如果dataObject已经从左值构建,相同的 std::forward将它作为左值传递,并根据需要产生复制构造函数调用:

Foo f{};
Event e{"Hello", f};

// ...

setData(std::forward<T>(dataObject)); // will call Foo's copy constructor

Complete demo


如果您想继续使用指向 void 的指针,您可以使用 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);
}
);
}

data被声明为 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 安全删除事件对象负载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40843719/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com