我正在尝试用C ++实现队列。队列中包含字符串。
我正在使用以下代码:
#include <iostream>
#include <stdlib.h>
struct qnode {
std::string data;//it stores a string
qnode *link;//and has a pointer to the next node in the structure.
};
class queue {
private:
qnode *front; // Pointer to first node.
qnode *rear; // Pointer to last node.
public:
// Constructor.
queue();
//Is the queue empty?
bool empty() ;
//peeks first element
int first(std::string *c);
// Adds element to the queue.
int enqueue(std::string c);
// Extracts an element from the queue.
int dequeue(std::string *c);
// Destructor.
~queue();
};
//////////// ACTUAL CODE /////////////
//Constructor, front and rear initially point to null.
queue::queue() {
front = NULL;
rear = NULL;
}
// If front points to null then the queue is empty.
bool queue::empty() {
return (front == NULL);
}
int queue::first(std::string *c) {
int res = 1;
if (!empty()) {
res = 0;
*c = front->data;
}
return res;
}
//Function for adding an element to the end of queue.
int queue::enqueue (std::string c){
int res = 1;
qnode *tmp; // Auxiliar pointer.
tmp = new qnode; //Auxiliar new node.
if(tmp != NULL) {
// If the queue is empty then we store the data in tmp and make its link point to null and front and rear become tmp.
if(empty()){
tmp->data = c;
tmp->link = NULL;
front = tmp;
rear = tmp;
rear->link = tmp;
} else {
// If it is not, then last node's link point to tmp, we store the data in tmp and tmp's link point to null.
rear->link = tmp;
tmp->data = c;
tmp->link = NULL;
// Rear of the queue points to tmp now.
rear = tmp;
}
res = 0;
}
return res;
}
// Function for extracting an element from the queue.
int queue::dequeue(std::string *c){
int res = 1;
// We make sure queue is not empty and access first node's data with the help of tmp.
if(!empty()) {
qnode *tmp;
tmp = front;
//The data extracted is stored in the character container c, passed by reference.
*c = tmp->data;
// Front now has to point to the next node and the old element has to be deleted.
front = front->link;
delete tmp;
res = 0;
}
return res;
}
// Destructor
queue::~queue() {
// If it is already empty we don't have to do anything.
if (empty()){
return;
}
qnode *tmp;
// We take front element and delete it throught a tmp node till the queue is empty.
while (!empty()){
tmp = front;
front = front->link;
delete tmp;
}
}
//////////// MAIN ///////////
int main(int argc, char *argv[]) {
queue queue;
std::string str2;
queue.enqueue("hello");
queue.dequeue(&str2);
std::cout << str2;
}
可悲的是,我收到以下错误:
$ g ++ -o issue issue.cpp $ ./issue issue(23060,0x7fff71058310)malloc: 对象0x7f962b403920的 *错误:未分配被释放的指针 * 在malloc_error_break中设置断点以调试helloAbort陷阱:6
如果我评论队列析构函数(“while”块),实现工作正常。使用char元素(只有单个字母),实现也很好。
任何人都可以启发我吗?
答案 0 :(得分:1)
你的问题是你正在初始化你的入队函数中的tmp后面的&gt;链接 - 将行rear->link = tmp
更改为rear->link = NULL
的空队列部分中的enqueue()
和它对我来说很好^^你的最后一个节点不应指向任何东西,当你到达目的地时,你试图删除相同的内存两次,因为那个内存与它自己有关。
如果一个类动态地管理资源,它应该提供一个复制构造函数,赋值运算符和析构函数。任何违反此规则的类(即你的)都有冒成指针副本的风险,然后删除它们。在您的示例中,它的外观如下:
您的队列使用您期望的字符串进行初始化。一切都很好看。然后调用函数l.insert(queue, 0)
,其原型如下:int insert(queue c, int position);
需要注意的重要一点是队列是通过VALUE传递的,这意味着它的COPY(使用复制构造函数 - 三大#1的规则),然后是列表节点使用THAT COPY的数据创建 - 特别是头指针。由于您没有提供复制构造函数,该指针只是按位复制 - 这意味着您的队列副本(您要插入到列表中的数据)具有EXACT SAME头指针作为您传入的队列,但是这是一个不同的对象。
然后,您编写了行tmp->data=c;
- 这是您的三大功能中的另一个,赋值运算符。同样,你没有提供一个 - 所以指针再次按位分配,导致与复制构造函数相同的问题。
到目前为止,我们已经传入了三个我们传入的队列对象副本,所有这些副本都有相同的头指针。看看我们要去哪里?
现在,当您的list::insert()
函数完成时,它会销毁它创建的所有本地对象。那是什么意思?它意味着调用您按值传递队列时创建的副本的析构函数。因为它具有与你拥有的那个队列的其他两个副本相同的头指针,所以它通过它们的列表以及它自己的列表,删除所有内容。所以现在,你的列表节点的队列数据是垃圾,但我们还没有完全崩溃和完全烧毁。让我们看看当你的程序退出时会发生什么。
现在有趣的开始了 - 你看到的双重免费错误(事实上,它是三重免费的,因为我们制作了三个副本,还记得吗?)。当你的程序退出时,其他副本的析构函数也会被调用 - 他们会尝试遍历列表,再次删除所有内容。
那么,我们如何解决这些问题呢?
提供的链接将为您提供更深入的解释 - 但您需要做的是确保这些类都提供三巨头规则中的所有功能,并且这些功能构成deep copy必要时 - 不是将指针复制到头部(例如),而是将队列设置为new qnode;
,将其设置为头部,并使用其他列表头部数据的副本填充其数据。例如(我没有编译它,并且没有错误检查,等等)你的队列复制构造函数可能如下所示:
Queue::Queue(const Queue &other)
{
head = new qnode; //IMPORTANT: don't copy the value of the pointer (like the compiler
// will by default if you let it)
head->data = other.head->data;
qnode *tmp = other.head->link;
while (tmp != nullptr)
{
qnode *tmp2 = new qnode; //again, don't copy the value of the pointer!
tmp2->data = tmp.data; //make a new node and copy the data
tmp2->link = nullptr;
rear->link = tmp2; //update the tail of the list
rear = tmp2;
}
}
希望有所帮助......它比我预期的打字更多!但是你给出了一个很好的例子,说明为什么三巨头规则是C ++中最重要的习语之一。每当你打破三巨头规则时,这些事情都会发生。