HW02
作业描述
修改 main.cpp,改良其中的双链表类 List:
避免函数参数不必要的拷贝 5 分
修复智能指针造成的问题 10 分
改用 unique_ptr 10 分
实现拷贝构造函数为深拷贝 15 分
说明为什么可以删除拷贝赋值函数 5 分
改进 Node 的构造函数 5 分
并通过 main() 函数中的基本测试。
如果你把 List 改成了基于迭代器的,或是作为模板 List: 只要是在 满足作业要求的基础 上,这是件好事! 老师会酌情加分,视为“独特的创新点”,但最多不超过 20 分。
思路
基于shared_ptr和weak_ptr去实现模板类:
首先源代码中Node节点的前驱和后继均为shared_ptr,这是一个会产生循环引用的典型情况,修改原则:拥有所属权的对象设置为shared_ptr,没有所属权的改为weak_ptr。在链表中,当前节点对下一个节点拥有所属权,对上一节点无所属权,对节点的定义进行修改如下(个人认为改成unique_ptr意义不大):
template <typename T>
struct Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev;
T value;
// 防止隐式类型转化
explicit Node(T val) : value(val) {}
void insert(T val) {
auto node = std::make_shared<Node>(val);
node->next = next;
node->prev = prev;
if (!prev.expired())
prev.lock()->next = node;
if (next)
next->prev = node;
}
void erase() {
if (!prev.expired())
prev.lock()->next = next;
if (next)
next->prev = prev;
}
~Node() {
printf("~Node()\n");
}
};
修改List类(也改成模板类),改成浅拷贝很容易,然后在这个类里面,原来的代码删除了拷贝赋值函数,但是List a = {}
仍然可以成功运行,是由于编译器自动调用了移动赋值函数,相当于List a = List({})
,右边是一个临时对象,会当成右值引用,然后调用移动赋值函数。
template <typename T>
struct List {
std::shared_ptr<Node<T>> head;
List() = default;
List(List const &other) {
printf("List 被拷贝!\n");
//head = other.head; // 这是浅拷贝!
// 请实现拷贝构造函数为 **深拷贝**
auto other_cur = other.head;
head = std::make_shared<Node<T>>(other_cur->value);
other_cur = other_cur->next;
std::shared_ptr<Node<T>> cur = head;
while (other_cur) {
auto tmp = std::make_shared<Node<T>>(other_cur->value);
cur->next = tmp;
tmp->prev = cur;
cur = tmp;
other_cur = other_cur->next;
}
}
List &operator=(List const &) = delete; // 为什么删除拷贝赋值函数也不出错
// 编译器会自动调用移动赋值函数
List(List &&) = default;
List &operator=(List &&) = default;
Node<T> *front() const {
return head.get();
}
T pop_front() {
int ret = head->value;
head = head->next;
return ret;
}
void push_front(int value) {
auto node = std::make_shared<Node<T>>(value);
node->next = head;
if (head)
head->prev = node;
head = node;
}
Node<T> *at(size_t index) const {
auto curr = front();
for (size_t i = 0; i < index; i++) {
curr = curr->next.get();
}
return curr;
}
};
当然print
函数也要改成模板函数:
template <typename T>
void print(const List<T>& lst) { // 有什么值得改进的?
printf("[");
for (auto curr = lst.front(); curr; curr = curr->next.get()) {
// printf(" %d", curr->value);
std::cout << " " << curr->value;
}
printf(" ]\n");
}
改成基于迭代器这点没看太明白,求教。