基于链表实现Queue类的三法则实现疑问求助
问题解答
核心结论
必须先为List类正确实现析构函数、拷贝构造函数、赋值运算符(即三法则),之后Queue类无需手动重复实现这些函数——编译器生成的默认版本就能正常工作,因为它们会自动调用List类的对应方法。
原因分析
Queue的核心状态完全依赖私有成员List list,所有内存管理的责任都在List身上:
- 如果
List没有实现深拷贝的拷贝构造/赋值运算符,直接拷贝Queue会导致两个Queue共享同一份链表内存,修改其中一个会影响另一个,销毁时还会重复释放内存引发崩溃。 - 如果
List没有析构函数,链表节点的内存会永远泄漏,哪怕Queue写了析构函数也无法解决底层的泄漏问题。
具体实现步骤
1. 先完善List类的三法则
假设List是单向链表结构,示例实现如下:
#include <algorithm> // 用于std::swap class List { private: struct Node { Item data; Node* next; Node(Item val) : data(val), next(nullptr) {} }; Node* head; Node* tail; public: // 默认构造函数 List() : head(nullptr), tail(nullptr) {} // 拷贝构造函数:深拷贝所有节点 List(const List& other) : head(nullptr), tail(nullptr) { Node* current = other.head; while (current != nullptr) { push_back(current->data); current = current->next; } } // 赋值运算符:使用拷贝交换 idiom,简洁且安全 List& operator=(List other) { // 传值参数触发拷贝构造 std::swap(head, other.head); std::swap(tail, other.tail); return *this; } // 析构函数:释放所有链表节点 ~List() { Node* current = head; while (current != nullptr) { Node* next_node = current->next; delete current; current = next_node; } head = tail = nullptr; } // 供Queue调用的辅助方法 void push_back(Item val) { Node* new_node = new Node(val); if (empty()) { head = tail = new_node; } else { tail->next = new_node; tail = new_node; } } void pop_front() { if (empty()) return; Node* temp = head; head = head->next; delete temp; if (head == nullptr) tail = nullptr; } Item& front() { return head->data; } bool empty() const { return head == nullptr; } };
2. 简化Queue类的实现
当List的三法则正确实现后,Queue可以直接依赖编译器生成的默认函数:
class Queue { private: List list; public: // 显式声明使用默认构造函数(可选,编译器会自动生成) Queue() = default; // 拷贝构造函数:默认版本会调用List的拷贝构造 Queue(const Queue& other) = default; // 赋值运算符:默认版本会调用List的赋值运算符 Queue& operator=(const Queue& other) = default; // 析构函数:默认版本会调用List的析构函数 ~Queue() = default; // 已实现的业务方法,直接复用List的功能 void push(Item val) { list.push_back(val); } void pop() { list.pop_front(); } Item& peek() { return list.front(); } bool empty() { return list.empty(); } };
3. 若要手动实现Queue的三法则(可选)
如果需要自定义逻辑(比如额外的状态跟踪),可以手动实现,但核心还是调用List的对应方法:
// 拷贝构造函数 Queue(const Queue& other) : list(other.list) { // 这里利用List的拷贝构造初始化成员 } // 赋值运算符 Queue& operator=(const Queue& other) { if (this != &other) { // 避免自赋值 list = other.list; // 调用List的赋值运算符 } return *this; } // 析构函数 ~Queue() { // 无需额外操作,List的析构会自动释放链表内存 }
内容的提问来源于stack exchange,提问作者Radical Edward
相关产品推荐
相关产品推荐

