从空队列中删除元素?

时间:2013-02-17 21:04:01

标签: c++

我正在编写优先队列的抽象数据类型作为大学的任务,其他人将使用。我的类中有一个函数dequeue,它删除队列中的第一个元素并返回该元素的数据。但是,当我尝试从空队列中删除元素时,程序崩溃了。我该怎么办?

如果有帮助的话,这是代码:

#ifndef PRIORITYQUEUE_H
#define PRIORITYQUEUE_H

#include <iostream>

using namespace std;

const int max_queue_items = 1000;

template<class T>
struct node{
    T data;
    int priority;
    node *next;
};

template<class T>
class PriorityQueue
{
    public:
        /*
            Constructor that creates an empty queue.
        */
        PriorityQueue(){
            head = NULL;
            size = 0;
        }

        /*
            Adds an element to the queue.

            Params:
            data - data of the element
            priority - priority of the element
        */
        bool is_empty(){
            if (size == 0){
                return true;
            }

            return false;
        }

        bool is_full(){
            if (size == max_queue_items){
                return true;
            }

            return false;
        }

        /*
            Adds an element to thq queue.
            It gets inserted before the first element with
            lower priority.
        */
        void enqueue(T data, int priority){
            node<T> * previous = NULL;
            node<T> * now = head;

            while (now != NULL && now->priority >= priority){
                previous = now;
                now = now->next;
            }

            node<T> * new_element = new node<T>;
            new_element->data = data;
            new_element->priority = priority;
            new_element->next = now;

            if (previous == NULL){
                head = new_element;
            } else {
                previous->next = new_element;
            }

            size++;
        }

        /*
            Removes the first element in the queue
        */
        T dequeue(){
            T data;

            if (!is_empty()){
                node<T> * now = head;
                data = now->data;
                head = head->next;
                delete now;

                size--;
            }

            return data;
        }

        /*
            Returns the priority of the first element.
            It's always the highest priority in the queue.
        */
        int get_first_priority(){
            return head->priority;
        }

        /*
            Returns the data of the first element in the queue.
        */
        T get_first_value(){
            if (is_empty())
                throw 0;

            return head->data;
        }

        /*
            Returns the number of elements in the queue.
        */
        int get_size(){
            return size;
        }

        /*
            Deletes the whole queue from the memory.
        */
        void flush(){
            node<T> * now;

            while (head != NULL){
                now = head;
                head = head->next;
                delete now;
                size--;
            }
        }

        /*
            Prints the whole queue following this format:
            data(priority)
        */
        void print(){
            node<T> * now = head;

            while (now != NULL){
                cout << now->data << "(" << now->priority << ")" << endl;
                now = now->next;
            }
        }

    private:
        node<T> * head; // Pointer to the head of the queue
        int size; // Number of elements in the queue
};

#endif // PRIORITYQUEUE_H

3 个答案:

答案 0 :(得分:1)

这可能是也可能不是您问题的根源,但我肯定会认为这是一个问题。在函数dequeue()中,当T返回is_empty()时,您可能会返回未初始化的变量(如果true不是类类型):

    T dequeue()
    {
        T data; // Uninitialized if T is not a class type

        if (!is_empty())
        {
            node<T> * now = head;

            //--------------------------------------------------------------
            // This initialization is skipped when `is_empty()` returns true
            data = now->data;
            //--------------------------------------------------------------

            head = head->next;
            delete now;

            size--;
        }

        return data;
    }

根据您对此函数返回的值以及T的类型执行的操作,您的程序可能具有未定义的行为(我可以想象T是您稍后取消引用的指针类型)

您可能希望将函数的第一行更改为:

T data = T();

它强制执行data对象的值初始化。如果T是类类型,则将调用默认构造函数。否则,data将被初始化为零。

调用dequeue()的函数应该在使用它之前检查返回的值(或者更好的是,在尝试从中弹出值之前,在队列上调用is_empty()以检查它是否为空)。

您甚至可以考虑在调用dequeue()并且队列为空时抛出异常:

T dequeue()
{
    if (is_empty())
    {
        // Requires including the <stdexcept> standard header
        throw std::logic_error("Queue is empty"); 
    }

    node<T> * now = head;
    T data = now->data;
    head = head->next;
    delete now;

    size--;

    return data;
}

客户端现在负责确保永远不会在空队列上调用dequeue()(或者它们将对dequeue()的调用包装到try/catch块中以处理可能抛出的异常。

另一种可能性是将bool返回给您的客户端,指示该值是否已成功弹出,可能将弹出元素分配给通过引用传递的参数:

bool dequeue(T& data)
{
    if (is_empty())
    {
        return false;
    }

    node<T> * now = head;
    data = now->data;
    head = head->next;
    delete now;

    size--;

    return true;
}

这样,客户端负责检查函数的结果。如果函数返回false,则data变量将初始化为客户端初始化它的任何内容。处理错误情况的责任再次转移给客户。

答案 1 :(得分:1)

我认为有一些问题。

首先也是最重要的,班级没有析构函数。如果不是所有元素都在程序中出列,那么就会出现内存泄漏。编写析构函数或使用智能指针代替原始指针。

其次,正如@Andy Prowl(谁知道如何在Twitter上发帖的人?)说,应该考虑未初始化的局部变量。 T data = T()适用于内置和自定义类型。

第三,我认为队列有容量限制max_queue_items但是队列部分没有相应的代码。

尽管如此,我认为所有这些缺陷都不会导致正常情况下的严重崩溃。也许问题出现在您的代码中调用类,并且对于未初始化的返回值的错误处理会导致崩溃。

答案 2 :(得分:0)

我看到你出现的唯一潜在问题是你正在创建一个未知类型T的临时变量。如果你在优先级队列中存储没有默认构造函数的类型的数据,你将遇到问题当你的dequeue调用并尝试默认构造该变量时。

如果是这种情况,我建议您重新设置优先级队列以保存指向模板类型的指针而不是数据本身。