【问题标题】:Can I use shared_ptrs in this example?我可以在这个例子中使用 shared_ptrs 吗?
【发布时间】:2014-05-06 16:54:05
【问题描述】:

我有一个简单的事件处理系统,它给我带来了问题。为了使用它,我继承自 EventHandler 类。然后构造函数在构造时注册每个对象。

这是EventHandler的构造函数:

EventHandler::EventHandler()
{
   EventDispatcher::getInstance().registerListener(this);
}

这会调用EventDispatcherregisterListener() 成员函数,该函数将其存储在向量中。

void EventDispatcher::registerListener(EventHandler* listener) 
{
   mListenerList.push_back(listener);
}

mLisernerList 长这样在哪里:

vector<EventHandler*> mListenerList;

EventDispatcher 只需在向量的每个元素上调用 sendEvent() 即可将事件通知它。

让我举个例子来说明我的问题。假设我的类Buttons 继承自EventHandler。我将在堆上创建按钮对象,然后将指向所有按钮的智能指针放在一个向量中。

vector<unique_ptr<Buttons>> mButtons;   
mButtons.push_back(unique_ptr<Buttons>(new Button()));

我最终会在 mButtons 中得到一个 unique_ptrs 向量,在 mListenerList 中得到一个指向相同动态分配的 Button 对象的原始指针向量。我不希望智能指针和原始指针指向同一个对象。

理想情况下,我希望 mButtons 中的 shared_ptrs 向量和 mListenerList 中的 weak_ptrs 向量指向动态分配的 Button 对象,同时允许 EventHandler 在创建时注册每个对象。这可能吗?

【问题讨论】:

  • 有可能。你可能想看看std::enable_shared_from_this
  • @OmnipotentEntity 我意识到我需要使用 enable_shared_from_this 来避免这个问题。虽然这可能是更微妙的问题,但我只是难以保持 EventHandler 的当前行为,但将其扩展为共享指针。
  • 智能指针和原始指针指向同一个对象有什么问题?只要智能指针“拥有”而原始指针没有。
  • @Chris Drew 我遵循了这个建议,“如果你想获得智能指针的全部好处,你的代码应该避免使用原始指针来引用相同的对象;否则太容易拥有悬空指针或双重删除的问题......混合使用一些内置指针可能很有用,但前提是你非常小心,如果它们的对象已被删除,它们就不可能被使用,而且你永远不会,使用它们来删除对象或以其他方式与它们一起行使对象的所有权。我的建议是......永远不要混合它们。”
  • @user870130:恕我直言,这是个坏建议。我认为the conventional wisdom from gurus like Herb Sutter 是智能指针非常适合“拥有”指针(或在weak_ptr 的情况下“可选地”拥有),但是当您知道对象将存活时,原始指针仍然最适合非拥有指针指针。智能指针不是灵丹妙药,您仍然需要清楚地了解所有权,否则您会遇到问题,可能不是悬空指针或双重删除,而是性能不佳或内存泄漏等问题。

标签: c++ shared-ptr smart-pointers weak-ptr


【解决方案1】:
class EventHandler {
private
    EventHandler(); //make the constructors protected, also in derived when possible

    template<class T, class...Us> //and make this function a friend
    friend std::shared_ptr<EventHandler> make_event(Us...us);
};
//this is the function you use to construct Event objects
template<class T, class...Us>
std::shared_ptr<T> make_event(Us...us)
{
    auto s = std::make_shared<T>(std::forward<Us>(us)...);
    EventDispatcher::getInstance().registerListener(s);
    return s;
}

这会调用 EventDispatcher 的 registerListener() 成员函数,该函数将其存储在向量中。

void EventDispatcher::registerListener(std::weak_ptr<EventHandler> listener) 
{
   mListenerList.push_back(listener);
}

mLisernerList 长这样在哪里:

vector<std::weak_ptr<EventHandler>> mListenerList;

EventDispatcher 只需在向量的每个元素上调用 sendEvent() 即可将事件通知它。

让我举个例子来说明我的问题。假设我的类 Buttons 继承自 EventHandler。我将在堆上创建按钮对象,然后将指向所有按钮的智能指针放在一个向量中。

vector<std::shared_ptr<Buttons>> mButtons;   
mButtons.push_back(make_event<Buttons>());

【讨论】:

    【解决方案2】:

    你不能天真地使用std::shared_ptr;有一些特别的 支持可能允许它,但它过于复杂,并且 几乎可以肯定在这里不合适;不可能 EventDispatcher 通常会“拥有”EventHandler

    这里真正的问题是您为什么要使用 smart 指针在这里? EventHandler 在其注册 构造函数,并在其析构函数中注销。中的指针 EventDispatcher 纯粹用于导航。一样的东西 可能适用于mButtons,尽管有些设计在 情况可能并非如此。 (我倾向于有点怀疑 unique_ptr 的向量。这取决于向量在哪里 位于,但据我所见,额外的复杂性 访问实际指针所需的复杂性大于 手动处理删除。)

    【讨论】:

    • 我需要在堆上创建一些对象以将它们保持在范围内。然后我想把它们保存在一个向量中,这样我就可以导航它们(你是对的)。最后,我有一个我想使用的事件处理程序系统。每一步似乎都如此简单和合乎逻辑。然而,在实践中,这似乎会造成混乱。您能提出更好的方法吗?
    • 名称(尤其是Button)暗示了一个GUI。通常,GUI 的组件将属于包含它们的组件;其他组件,例如 EventHandler(如果不是 GUI 组件之一的基类)将属于它们所服务的类。在某处注册的组件将在其析构函数中取消注册(并且注册表将使用原始指针)。因此,每个对象将属于单个其他对象。您可以为此使用unique_ptr,但如果它们位于向量中,您可能会发现手动执行必要的删除操作同样容易。
    猜你喜欢
    • 2019-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-15
    相关资源
    最近更新 更多