使用已实现的 LinkedList 模块在队列实现中复制构造函数问题

Copy Constructor issue in Queue Implementation using implemented LinkedList module

完整存储库:https://github.com/mervynlee94/queue/tree/master

我已经实现了 LinkedList 并毫无问题地测试了我的复制构造函数。

LinkedList<int> l1;
l1.insert(1,3);               // set value 3 in 1st position
l1.insert(2,7);               // set value 7 in 2nd position
l1.insert(3,9);               // set value 9 in 3rd position
LinkedList<int> l2(l1);       // copy constructor
l2.setEntry(2,5);             // set value 5 in position 2
cout<< l1.getEntry(2) <<endl; // output: 7
cout<< l2.getEntry(2) <<endl; // output: 5

我的队列实现使用这个 LinkedList 模块作为数据结构,代码如下。

#ifndef _LIST_QUEUE
#define _LIST_QUEUE
#include "QueueInterface.h"
#include "LinkedList.h"

template<class ItemType>
class ListQueue : public QueueInterface<ItemType> {
    private :
        LinkedList<ItemType> list; 
    public :
        ListQueue();
        ListQueue(const ListQueue& aQueue);
        ~ListQueue();
        bool isEmpty() const ;
        void enqueue( const ItemType& newEntry);
        void dequeue();
        ItemType peekFront() const;
};
#endif

template<class ItemType>
ListQueue<ItemType>::ListQueue() {

}

template<class ItemType>
ListQueue<ItemType>::ListQueue(const ListQueue& aQueue) {
    list = LinkedList<ItemType>(aQueue.list);
}

template<class ItemType>
ListQueue<ItemType>::~ListQueue() {}

template<class ItemType>
bool ListQueue<ItemType>::isEmpty() const {
    return list.isEmpty();
}

template<class ItemType>
void ListQueue<ItemType>::enqueue(const ItemType& newEntry) {
    list.insert(list.getLength()+1, newEntry);
}

template<class ItemType>
void ListQueue<ItemType>::dequeue() {
    list.remove(1);
}

template<class ItemType>
ItemType ListQueue<ItemType>::peekFront() const {
    return list.getEntry(1);
}

当我测试 main.cpp 中的代码时,我遇到了分段错误。

ListQueue<int> q1;
q1.enqueue(1);
q1.enqueue(2);
q1.enqueue(3);
ListQueue<int> q2(q1); // Set breakpoint here
q2.enqueue(5);
cout<<q2.peekFront()<<endl;
cout<<q1.peekFront()<<endl;

我在上面设置了一个调试断点,我意识到一件事。 Queue 实现中的复制构造函数无法正常工作。

aQueue.list 和 this.list 的复制构造函数中显示的调试值不同。

这是LinkedList复制构造函数的实现

template<class ItemType>
LinkedList<ItemType>::LinkedList(const LinkedList<ItemType>& aList) {
    Node<ItemType>* listPtr = aList.headPtr;
    if(listPtr == nullptr) {
        headPtr = nullptr;
    }
    else {
        headPtr = new Node<ItemType>(listPtr->getItem());
        Node<ItemType>* newPtr = headPtr;
        while(listPtr->getNext() != nullptr) {
            listPtr = listPtr->getNext();
            Node<ItemType>* newNode = new Node<ItemType>(listPtr->getItem());
            newPtr->setNext(newNode);
            newPtr = newPtr->getNext();
        }
    }
    itemCount = aList.getLength();
}

请注意,由于 LinkedList 的复制函数已经实现,您不必重新实现它 - C++ 会自动为您完成。

另一个重要的东西——拷贝构造函数和拷贝赋值运算符是distict

您为您的 Queue 实现了 Copy-ctor,并且在这个 copy-ctor 中使用了复制赋值运算符,我怀疑您没有实现它。

修复它的最简单方法是简单地删除复制构造函数和析构函数,它们不是必需的。调用复制构造函数的正确方法如下:


template<class ItemType>
ListQueue<ItemType>::ListQueue(const ListQueue& aQueue) 
    : list(aQuquq.list) {
}

我还有三点要说:

  1. 规则 3 - 如果您声明复制构造函数、复制赋值或析构函数之一,您几乎肯定必须实现它们。
  2. C++ 中有一个更复杂的特性,叫做 "move semantics",如果您熟悉这个术语,您应该考虑 5 的规则 - 相同作为先前的规则,添加了移动构造函数和移动赋值运算符。
  3. 您似乎在使用继承,但我在任何地方都没有看到关键字 override。如果您使用 C++11 或更高版本,请考虑将 override 限定符添加到覆盖方法中。

最后,C++11 支持默认 ctors/dtors 和赋值运算符。如果你写一个空的析构函数(就像你在这里做的那样),更简洁的语法是写

class ListQueue : public QueueInterface<ItemType> {
        ...
        ~ListQueue() = default;
        ...
};