【问题标题】:Removing from STL std::queue without destructing the removed object?从 STL std::queue 中删除而不破坏已删除的对象?
【发布时间】:2010-12-03 14:43:14
【问题描述】:

我可以在 STL 容器(队列和列表)上找到的所有文档都说,对于任何删除函数,都会调用已删除对象的析构函数。这意味着我不能在任何时候使用 std::queue 我想要一个队列,它只是一个需要对它们执行一些操作的对象列表。

我希望能够在对象排队等待我对它们做某事时将它们添加到队列中。然后,当我完成它们时,我想将它们从中删除,而不会破坏有问题的对象。从我读过的文档来看,这似乎是不可能的。我是否误读了文档?除了在调用 pop_front 时不调用已删除对象的析构函数的基本“队列”之外,STL 中是否还有另一种类型的队列?

编辑澄清:就我而言,我使用的是指针列表。像这样的:

   dbObject *someObject;
   queue<dbObject *> inputQueue;
   inputQueue.push_back(someObject);

   ...

   dbObject *objectWithInput = inputQueue.front();
   //handle object's input...
   inputQueue.pop_front(); // Remove from queue... destroyed now?

【问题讨论】:

标签: c++ stl queue


【解决方案1】:

STL 容器具有值语义。当您将对象推送到 STL 容器中时,STL 容器会保留它自己的对象副本,而当从容器中删除对象(内部副本)时,它会被销毁。

如果您使用代理类型的容器,如原始指针、智能指针(shared_ptr、weak_ptr)或适配器(如 boost::reference_wrapper),则 STL 容器将销毁代理而不是类型。选择一个而不是其他通常是您希望如何处理资源的问题。

最常见的习惯用法是使用原始指针,但它们没有明确谁负责销毁(从容器中提取的代码应该删除指针,或者资源在其他地方处理?)。

现代使用趋向于 shared_ptr 方法,因为它稀释了所有权问题。当您将对象从容器中取出时,将保证该对象处于活动状态,如果没有其他人持有 shared_ptr,则当本地 shared_ptr 超出范围时,该对象将自动被删除。使用 weak_ptr 将保留原始代码中的所有权,但允许您在使用前检查指针的有效性(如果它被删除)。这可以让您避免对将立即删除的对象执行操作。

shared_ptr/weak_ptr 方法的问题在于它强制您使用 shared_ptr 来保存原始资源。这意味着如果不重新设计类以通过 shared_ptr 保存属性,您将无法将指针放入另一个类的子对象(成员属性),这将产生其他含义(属性将不再在内存中连续,将需要更多的动态分配操作...)

一种很少见的技术是使用适配器作为 boost::reference_wrapper。引用包装器是一个代理对象,它包含对原始对象的引用,并且本身是可复制的。与普通原始指针相比的优势在于,读取代码很明显,资源是在队列之外管理的:从队列中提取数据的代码不需要删除对象。与智能指针方法相比的优势在于,您无需重新设计系统的其他部分即可使用智能指针。缺点是,与原始指针方法一样,您必须手动确保被引用对象的生命周期比容器中的引用长。

【讨论】:

  • 我喜欢你解释和措辞这个答案的方式,非常有用且非常完整。
【解决方案2】:

将容器中的项目想象为该容器的“范围内”,当从容器中删除项目时,就像离开函数的范围一样。如果变量是指针,则在离开范围时项目不会发生任何事情。如果变量是本地堆栈,则在离开作用域时将自动调用析构函数。

在容器中存储指针与分配到本地原始指针具有相同的缺点,内存不会自动清理。在一个函数中,如果你不删除指针或通过返回它来转移所有权,那么你就有内存泄漏。

在容器中存储原始指针时,所有权可能会变得有点模糊,并且很容易发生泄漏。查看 tr1::shared_ptr 并将它们存储在容器中。

C++0x 中的std::unique_ptr 也是一个很好的解决方案,可以在可用时将指针存储在stdlib 容器中。

【讨论】:

    【解决方案3】:

    如果您将指向对象的指针放入队列(以及任何其他 STL 容器)中,则当您删除它们时,这些指针不会被删除。

    详细说明:当您使用 std::queue 并删除对象时,将调用 some_obj* 的析构函数。但是普通指针(或任何 POD 类型 - int、char 等)的析构函数是空的,没有操作。这里的细线是 some_obj* 的析构函数与 some_obj 的析构函数非常不同。

    【讨论】:

    • 没错。仅供参考,如果您确实想要容器来删除指向的对象,那么您需要存储某种智能指针或使用类似 boost 的指针容器。
    【解决方案4】:
    class someobj_t {};
    
    std::queue<someobj_t> q;
    ...
    
    someobj_t ppd = q.front(); // ppd is not a reference
    q.pop();
    
    // ppd now contain removed object
    

    如果您不想复制someobj_t,可以使用std::queue&lt; shared_ptr&lt;someobj_t&gt; &gt;

    【讨论】:

    • 那么真实对象不会在擦除时被破坏。
    【解决方案5】:

    使用指向对象的指针列表怎么样?

    【讨论】:

    • 那还不叫解构器吗?这就是我正在使用的,我的大多数对象都是指向存储在代码中其他位置的对象的指针。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-10
    • 2012-01-20
    相关资源
    最近更新 更多