使用作用域限定的队列:: swap清空std :: queue是否违反任何规则?

时间:2019-05-16 21:16:52

标签: c++ scope queue

在很多情况下,都在使用队列,队列可能增长到数百个。不幸的是,如果有必要的话,没有一个解决队列空的解决方案。我想知道是否使用作用域队列进行交换,然后销毁作用域队列,是否破坏了任何内存分配/管理规则?

以下代码段是我建议的示例。如果长时间使用,似乎工作正常,不确定结果。

#include <cstdlib>
#include <iostream>
#include <queue>
int
main()
{
    std::queue<int> foo;
    foo.push(10);
    foo.push(20);
    foo.push(30);
    std::cout << "size of before foo: " << foo.size() << '\n';

    {
        std::queue<int> bar;
        swap(foo, bar);
    }
    std::cout << "size of after foo: " << foo.size() << '\n';
    return 0;
}

5 个答案:

答案 0 :(得分:5)

您的代码很好。 swap将使foo成为默认构造的std::queue,并且当bar在作用域末尾被销毁时,它将释放foo正在使用的内存。由于您没有使用newdelete,因此没有问题,因为std::queue“做正确的事”(RAII类型是一件很棒的事)

您已成功完成

std::queue<int>{std::move(foo)}; // move foo into a temporary that is immediately destroyed to release the storage

但是您的方法为您提供了foo状态的更强有力的保证。您的方法将foo保留为默认的构造状态,而上述方法将其保留为有效但未指定的状态。


另一种选择是使用Is there a way to access the underlying container of STL container adaptors?中提供的解决方案之一来从foo获取基础容器并对其进行调用clear。看起来像

#include <cstdlib>
#include <iostream>
#include <queue>

// function from https://stackoverflow.com/a/29325258/4342498 by jxh: https://stackoverflow.com/users/315052
template <class ADAPTER>
typename ADAPTER::container_type & get_container (ADAPTER &a)
{
    struct hack : ADAPTER {
        static typename ADAPTER::container_type & get (ADAPTER &a) {
            return a.*&hack::c;
        }
    };
    return hack::get(a);
}

int main()
{
    std::queue<int> foo;
    foo.push(10);
    foo.push(20);
    foo.push(30);
    std::cout << "size of before foo: " << foo.size() << '\n';

    get_container(foo).clear();

    std::cout << "size of after foo: " << foo.size() << '\n';
    return 0;
}

答案 1 :(得分:2)

这不仅是完全安全的,而且还是容器的移动构造函数通常如何工作的方式:通过与寿命短(或至少即将被破坏)的另一个对象交换,然后让另一个对象死亡。然后,析构函数会尽快为您执行所有数据清除。 (此处可解决缺少clear()成员函数的问题。)

我认为是否需要一次“清除”操作,而我真的想使用队列(例如,像您所说的那样强制FIFO),那么我会做同样的事情。

尽管如果您可以让旧容器超出范围并切换到使用新声明的容器,那就更好了。

答案 2 :(得分:1)

不应违反任何规则,但您可以这样做

e.g 1, 2, 3, 4, ...17

答案 3 :(得分:1)

即使长时间进行很多次也没关系。如果销毁非空队列是合法的,并且交换队列是合法的,则交换并销毁是合法的。

我想说的是,将队列(或与此相关的任何其他容器)交换到临时对象是在线程感知上下文中清理容器的首选方法:

void MyClass::Reset()
{
    // ...
    {
        Container      tmp;
        {
            std::scoped_lock     lock(m_mutex);
            tmp.swap(m_container);
        }
    }
    // ...
}

原因是

  1. 更短的关键部分。因为必须在相当短的交换操作期间获得同步锁定。
  2. 缩小了死锁范围。由于释放锁后将调用包含对象的析构函数。但这当然取决于那些析构函数可以调用什么。

答案 4 :(得分:1)

不需要范围:

std::queue<int>().swap(foo);

但是,否则,您的代码有效。

在这里,我们创建一个临时的空队列。然后,我们将其状态与具有大量状态的队列交换;因此foo没有状态,临时队列具有foo的旧状态。

然后,在语句末尾,我们销毁临时队列以及foo的旧状态。

还有一个简短的说明适用于每种类型:

template<class T>
void clear( T& t ) {
  using std::swap;
  swap( static_cast<T&>(T()), t );
}

那不是普通的数据。检测我们想要对T进行零初始化的情况非常棘手。

无法默认构造的()对象将无法在此处编译。