C ++使用shared_ptr安全地删除事件对象的负载 [英] C++ safely deleting event object payload with shared_ptr

查看:165
本文介绍了C ++使用shared_ptr安全地删除事件对象的负载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要创建一个事件侦听器系统调度的事件对象。 事件需要具有以下属性:

I need to create an Event object to be dispatched by an event listener system. The Event needs to have following properties:


  1. 事件可以由0..n监听器对象处理。

  2. 事件包含一个void指针,指向任意对象(有效载荷)(构建时为未知类型)。事件侦听器将根据事件的名称转换为适当的类型。

  3. 需要有效载荷对象(自动)删除一次事件已派到有关方面。

  4. 假设在事件处理时,侦听器可以制作浅色的有效载荷副本。

  1. Event can potentially be handled by 0..n listener objects.
  2. 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 on Event's name.
  3. 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.
  4. 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 嵌入一个具有约束力的删除器:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆