Skip to content

hw02 模板、迭代器 #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,29 @@

## 作业要求

修改 main.cpp,改良其中的双链表类 `List`:

- 避免函数参数不必要的拷贝 5 分
- `print()`和`push_front()`改为常量参数传递,避免拷贝

- 修复智能指针造成的问题 10 分
- `shared_ptr`循环引用造成内存无法释放

- 改用 `unique_ptr<Node>` 10 分
- 实现拷贝构造函数为深拷贝 15 分
- 使用迭代器遍历目标,开辟新内存并赋值

- 说明为什么可以删除拷贝赋值函数 5 分
- 编译器会自动创建一个纯右值,再用移动构造将所有权转给等号左边的变量,相当于调用了右值构造+移动赋值

- 改进 `Node` 的构造函数 5 分
- 声明为explicit,防止单参数隐式构造,初始列构造减少一次赋值

## 模板

List和Node类都实现为模板类,并用`List<double>`测试成功

## 迭代器

并通过 `main()` 函数中的基本测试。
实现了`list_iterator`,拷贝构造时深拷贝的遍历操作使用迭代器完成

## 关于内卷

Expand Down
252 changes: 154 additions & 98 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,118 +2,174 @@
#include <cstdio>
#include <memory>

struct Node {
// 这两个指针会造成什么问题?请修复
std::shared_ptr<Node> next;
std::shared_ptr<Node> prev;
// 如果能改成 unique_ptr 就更好了!

int value;

// 这个构造函数有什么可以改进的?
Node(int val) {
value = val;
}

void insert(int val) {
auto node = std::make_shared<Node>(val);
node->next = next;
node->prev = prev;
if (prev)
prev->next = node;
if (next)
next->prev = node;
}

void erase() {
if (prev)
prev->next = next;
if (next)
next->prev = prev;
}

~Node() {
printf("~Node()\n"); // 应输出多少次?为什么少了?
}
template <class value_type>
struct Node
{
std::unique_ptr<Node> next;
Node* prev;
// 如果能改成 unique_ptr 就更好了!

value_type value;

// 这个构造函数有什么可以改进的?:防止单参数隐式构造,初始列构造减少一次赋值
explicit Node(value_type val) : value(val)
{}

void erase() {
if (next)
next->prev = prev;
if (prev)
prev->next = std::move(next);
}

~Node() {
printf("~Node() : %f\n", value); // 应输出多少次?为什么少了?:14次,share_ptr循环引用导致内存无法释放
}
};

struct List {
std::shared_ptr<Node> head;

List() = default;

List(List const &other) {
printf("List 被拷贝!\n");
head = other.head; // 这是浅拷贝!
// 请实现拷贝构造函数为 **深拷贝**
}

List &operator=(List const &) = delete; // 为什么删除拷贝赋值函数也不出错?

List(List &&) = default;
List &operator=(List &&) = default;

Node *front() const {
return head.get();
}

int pop_front() {
int ret = head->value;
head = head->next;
return ret;
}

void push_front(int value) {
auto node = std::make_shared<Node>(value);
node->next = head;
if (head)
head->prev = node;
head = node;
}

Node *at(size_t index) const {
auto curr = front();
for (size_t i = 0; i < index; i++) {
curr = curr->next.get();
}
return curr;
}

template <class T>
struct List
{
using node_type = Node<T>;

class list_iterator
{
public:
using iterator_catagory = std::bidirectional_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = node_type;
using pointer = node_type*;
using reference = node_type&;

list_iterator(pointer ptr) : m_ptr(ptr)
{}

reference operator*() { return *m_ptr; }
pointer operator->() { return m_ptr; }

list_iterator operator++() {
m_ptr = m_ptr->next.get();
return *this;
}

list_iterator operator++(int) {
return ++(*this);
}

friend bool operator==(const list_iterator a, const list_iterator b) {
return a.m_ptr == b.m_ptr;
}

friend bool operator!=(const list_iterator a, const list_iterator b) {
return a.m_ptr != b.m_ptr;
}

private:
node_type* m_ptr;
};

public:
std::unique_ptr<node_type> head;
node_type* back;
List() = default;

List(List const& other) {
printf("List 被拷贝!\n");

// 请实现拷贝构造函数为 **深拷贝**
head = std::make_unique<node_type>(other.cbegin()->value);
back = head.get();
for (auto it = ++other.cbegin(); it != other.cend(); ++it) {
push_back(it->value);
}
}

List& operator=(List const&) = delete; // 为什么删除拷贝赋值函数也不出错?:编译器自动创建一个纯右值,再用移动构造将所有权转给等号左边的变量

List(List&&) = default;
List& operator=(List&&) = default;

node_type* front() const {
return head.get();
}

T pop_front() {
T ret = head->value;
head = std::move(head->next);
return ret;
}

void push_front(const T& value) {
auto node = std::make_unique<node_type>(value);
if (head)
head->prev = node.get();
else
back = node.get();
node->next = std::move(head);
head = std::move(node);
}

void push_back(const T& value) {
auto node = std::make_unique<node_type>(value);
node->prev = back;
back->next = std::move(node);
back = back->next.get();
}

node_type* at(size_t index) const {
auto curr = front();
for (size_t i = 0; i < index; i++) {
curr = curr->next.get();
}
return curr;
}

list_iterator cbegin() const { return list_iterator(head.get()); }

list_iterator cend() const { return list_iterator(nullptr); }

//TODO:非const的begin(),end()
//TODO:shared_ptr改unique_ptr
//TODO:end(),back实现修改,返回值依然是nullptr,但可以--
};

void print(List lst) { // 有什么值得改进的?
printf("[");
for (auto curr = lst.front(); curr; curr = curr->next.get()) {
printf(" %d", curr->value);
}
printf(" ]\n");
template <class T>
void print(List<T> const& lst) {
// 有什么值得改进的?:常量引用传递避免拷贝
printf("[");
for (auto curr = lst.front(); curr; curr = curr->next.get()) {
printf(" %f", curr->value);
}
printf(" ]\n");
}

int main() {
List a;
List<double> a;

a.push_front(7);
a.push_front(5);
a.push_front(8);
a.push_front(2);
a.push_front(9);
a.push_front(4);
a.push_front(1);
a.push_front(7.2f);
a.push_front(5.5f);
a.push_front(8.2f);
a.push_front(2.1f);
a.push_front(9.3f);
a.push_front(4.5f);
a.push_front(1.9f);

print(a); // [ 1 4 9 2 8 5 7 ]
print(a); // [ 1 4 9 2 8 5 7 ]

a.at(2)->erase();
a.at(2)->erase();

print(a); // [ 1 4 2 8 5 7 ]
print(a); // [ 1 4 2 8 5 7 ]

List b = a;
List<double> b = a;

a.at(3)->erase();
a.at(3)->erase();

print(a); // [ 1 4 2 5 7 ]
print(b); // [ 1 4 2 8 5 7 ]
print(a); // [ 1 4 2 5 7 ]
print(b); // [ 1 4 2 8 5 7 ]

b = {};
a = {};
b = {};
a = {};

return 0;
return 0;
}