这是C++ programming II这门课布置的一个小作业,核心是关于动态存储和指针的理解(尤其是new和delete的使用);花了不少时间,中途来csdn找资料也没有看到相关内容的经验分享,所以来写篇博文记录一下成果和心得。
代码旁有简单的注释,中英夹杂;不重要的我没有翻译,有问题的话欢迎评论留言。
首先声明一些习惯性用词:
head:头节点
tail:尾节点/尾部
LinkedList.h:
#ifndef INTLIST_H
#define INTLIST_H
#include
using namespace std;
struct IntNode {
int value;
IntNode *next;
IntNode(int value) : value(value), next(nullptr) {}
};
class IntList {
private:
IntNode *head;
IntNode *tail;
public:
IntList();
IntList(const IntList &cpy);
~IntList();
void push_front(int);
void pop_front();
bool empty() const;
const int & front() const;
const int & back() const;
IntList& operator=(const IntList &rhs);
void push_back(int);
void clear();
void selection_sort();
void insert_ordered(int);
void remove_duplicates();
friend ostream & operator<<(ostream &, const IntList &rhs);
};
#endif
LinkedList.cpp里的构造函数:
IntList::IntList(): head(nullptr), tail(nullptr) {}
最关键的来了,不注意的话从这一步开始就已经产生memory leak和dangling pointer了
IntList::~IntList(){
if (head != nullptr){
// 1. go to head
// 2. delete head and set it to null
// 3. use the next of head to go to the next node in the IntList
// 4. delete that node and set it to null
// 5. repeat 3 and 4 until it reaches tail
while(head != nullptr){
IntNode *tmp = head->next; // 从头节点的next一直走到nullptr
delete head;
head = tmp; // 终止时头节点一定是nullptr
}
}
}
此时注意考虑三种情况:
void IntList::push_front(int num){
IntNode* node = new IntNode(num);
if (empty()){
//consider three different senarios: 1. empty; 2. only one node(head == tail == nullptr); 3. two or more than two
tail = node;
head = node;
}else{
node->next = head;
head = node;
}
} // 为什么我的代码这么丑?难受
}
void IntList::push_back(int value_){
IntNode* node = new IntNode(value_);
if (head == nullptr){
push_front(value_);
}
else{
tail->next = node;
tail = node;
}
}
const int & IntList::front() const{
return head->value;
}
const int & IntList::back() const{
return tail->value;
}
bool IntList::empty() const{
return (head == nullptr); // if checking both nodes, when deleting, set to null
}
void IntList::pop_front(){
// the same three senarios
if (!empty()){
if (head != nullptr){
IntNode* node = head->next;
delete head;
head = node;
}
else if(head == tail){
delete head;
delete tail;
head = nullptr;
tail = nullptr; // avoid the dangling pointer
}
}
}
void IntList::clear(){
if (head != nullptr){
// 1. delete head and set it to null
// 2. use the next of head to go to the next node in the IntList
// 3. delete that node and set it to null
// 4. repeat 2 and 3 until it reaches tail
while(head != nullptr){
IntNode *tmp = head->next;
delete head;
head = tmp;
}
}
}
注意这里我是用节点的next是否为nullptr来判断是否已经到了尾部(因为我在下一部分的的remove_duplicates()函数里不知只剩一个节点时tail如何进行处理,检查节点是否== tail时会打印出错)
ostream & operator<<(ostream &out, const IntList &list){
IntNode* node = list.head;
while(node != nullptr){
out << node->value;
if (node->next == nullptr){
// test deleting tail success or not; since I cannot handle the positon of tail node properly, this is a good practice
break;
}
out << " ";
node = node->next;
}
return out;
}
要进行 deep copy哦,需要手动一个个增加节点
IntList::IntList(const IntList &cpy){
head = nullptr; // !
tail = nullptr; // !
if (this != &cpy){
if (!cpy.empty()){
IntNode* tmp = cpy.head;
while(tmp != nullptr){
push_back(tmp->value);
tmp = tmp->next;
}
}
} // 为什么我的代码长得这么丑......难受
}
此时注意需要考虑五种情况:
IntList& IntList::operator=(const IntList &rhs){
if (rhs.empty()){
head = nullptr;
tail = nullptr;
}
else if (&rhs != this){
if (head != nullptr){
clear();
head = nullptr;
tail = nullptr; // clear() does not set head and tail to null which can be used later
}
IntNode* tmp = rhs.head;
while(tmp != nullptr){
push_back(tmp->value);
tmp = tmp->next;
}
}else if (&rhs == this){
return *this;
}
return *this;
}
这里我犯过两个错误:
IntNode* tmp = rhs.head;
// do not assign head with rhs's head, the push_back function does the same
// 如果加上 head = rhs.head;, 会产生segmentation fault (访问dangling pointer时产生;
// dangling pointer: deallocates the data the pointer pointing to without modifying the value of the pointer)
while(tmp != nullptr){
push_back(tmp->value);
tmp = tmp->next;
}
剩下的还有selection_sort(), insert_ordered(int), remove_duplicates()以及写作业时程序报过的所有error;今天作业太多了,有时间再写在下一篇里。
附上复习后面三个函数的实现和报错分析的 Notion 笔记链接,全英文阅读无障碍的朋友可以来看看:
https://www.notion.so/lavendershuo/Review-Linked-List-19911524ae42403b8cab305ff36c1943