队列不是真正的窗口,而是一种容器适配器,专门设计用于在FIFO上下文中操作(先进先出),其中元素被插入到容器的一端,并从另一端提取。它包含了另外一种容器,且进行了包装,看其定义如下:

第二个参数指定了queue的底层实现类型,这里默认为 std::deque,也可以是其他任意类型,只要支持front, back, push_back, pop_front等操作。
今天主要看其的两个接口,因为之前一直有疑惑,即 front() 和 pop()。一个是获取队首元素, 一个是弹出队首元素。代码如下:
#include <queue>
#include <iostream>
#include <string.h>
class CData
{
public:
CData(int value): mValue(value)
{
}
CData(const CData& another)
{
mValue = another.mValue;
std::cout << "copy construtor" << std::endl;
}
~CData()
{
std::cout << "destructor" << std::endl;
}
void print()
{
std::cout << mValue << std::endl;
}
private:
int mValue;
};
int main()
{
std::queue<CData> dataQueue;
CData test1(10);
dataQueue.push(test1);
std::cout << "before front" << std::endl;
CData data = dataQueue.front();
dataQueue.pop();
std::cout << "finish front" << std::endl;
data.print();
return 0;
}
我们知道,调用 front() 返回的是一个引用,而代码实际运行结果为:

1,在调用 front() 时调用在类的拷贝构造函数,在调用 pop() 时调用了类的析构函数。疑惑的是front() 不是返回元素的引用吗?怎么会调用拷贝构造函数呢?原因为:front() 返回值赋值给的普通变量,变量 data 取到的应该是该元素的一份拷贝,如果用引用变量去接收,则不会调用拷贝构造函数,如改成:CData &data = dataQueue.front(); 其结果就不会调用拷贝构造函数,如:

2,在调用 pop() 时会调用元素的析构函数,队列大小减 1。但需要注意的是元素占用的内存并未释放,直到队列被析构。

我们从GDB 里看一看:

在运行到 CData &data = dataQueue.front(); 其内存地址是 0x400a50,成员数据随机的,再调用front()后,data指向了队列元素,其地址为:0x605060,打印出成员数据是 10,当执行完pop()后,data还是指向原地址,而CData的析构函数只是输出一行打印信息并没做其他操作。如果你类类型里有资源的申请(如new 或 malloc),而在析构里已经释放资源,在调用 pop() 后,千万在小心使用,如:
#include <queue>
#include <iostream>
#include <string.h>
class CData
{
public:
CData(int value): mValue(value)
{
mPtr = new char[20];
}
CData(const CData& another)
{
mValue = another.mValue;
std::cout << "copy construtor" << std::endl;
}
~CData()
{
std::cout << "destructor" << std::endl;
delete []mPtr;
}
void print()
{
std::cout << mValue << std::endl;
const char *str = "test";
memcpy(mPtr, str, strlen(str));
}
private:
int mValue;
char *mPtr;
};
int main()
{
std::queue<CData> dataQueue;
CData test1(10);
dataQueue.push(test1);
std::cout << "before front" << std::endl;
CData &data = dataQueue.front();
dataQueue.pop();
std::cout << "finish front" << std::endl;
data.print();
return 0;
}
在调用 pop() 后,还去调用 print() 接口,而此接口里却用到的指针,程序则会出现segmentation fault :


本文详细探讨了C++标准库中的队列容器适配器,重点分析了front()和pop()两个关键接口。front()返回队首元素的引用,若直接赋值给普通变量会触发拷贝构造函数,而引用则不会。pop()操作会删除队首元素并调用析构函数,但不释放内存。在使用队列时,尤其要注意在pop()后避免对已析构对象的操作,以防引发内存问题。
796

被折叠的 条评论
为什么被折叠?



