C# 的垃圾回收机制

GC 是什么?

GC,全称 Garbage Collection(垃圾回收),是 .NET 中负责自动内存管理的机制。它会追踪哪些对象仍在使用,哪些可以被释放,从而避免内存泄漏和野指针问题。

阅读全文
Sort Ⅱ

​ 几个排序算法

阅读全文
shared_ptr

总体思路

  • 使用类模板来实现多类型支持

  • 构造函数(动态创建)、析构函数(销毁对象)

  • 拷贝构造函数

  • 拷贝运算符(->、*)

  • 实现移动构造和移动运算符

  • 对于引用计数的操作服从智能指针的设计规则

整体框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template<class T>
class new_shared_ptr {
public:
new_shared_ptr();
new_shared_ptr(const new_shared_ptr& p); //构造

~new_shared_ptr(); //析构

new_shared_ptr& operator=(const new_shared_ptr<T>& obj); //拷贝运算符

new_shared_ptr(new_shared_ptr&& dying_obj); // 移动构造

new_shared_ptr& operator=(new_shared_ptr&& dying_obj); //移动赋值运算符

int operator->(); //->运算符

int operator*(); //* 运算符
private:
T* ptr;
int* ref_count;
mutex *m_pMutex;
}

构造函数

1
new_shared_ptr(): ptr(new T), ref_count(new int(1)), mutex(new mutex) {}

拷贝构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
new_shared_ptr(const new_shared_ptr& p) {
//先对当前对象进行初始化
ptr = p.ptr;
ref_count = p.ref_count;
mutex = p.mutex;
//判断拷贝进来的指针是否指向空,不指向空表明当前地址多了一个指向,引用计数加一
if (p.ptr != nullptr) {
m_pMutex->lock();
(*ref_count)++;
m_pMutex->unlock();
}
}

重载=、->、* 运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
new_shared_ptr& operator=(const new_shared_ptr<T>& obj) {
//检查自赋值
if (obj.ptr == ptr) return *this;
//防止空指针指向别处
if (ptr) {
//自身指向即将改变,判断原先指向的地址是否销毁
m_pMutex->lock();
(*ref_count)--;
m_pMutex->unlock();
if (*ref_count == 0) {
delete ptr;
delete ref_count;
delete m_pMutex;
}
ptr = obj.ptr;
ref_count = obj.ref_count;
m_pMutex = obj.m_pMutex;
m_pMutex->lock();
(*ref_count)++;
m_pMutex->unlock();
}
return *this;
}
T* operator->() return ptr;
T& operator*() return *ptr;
移动构造

​ 当使用移动构造函数创建一个新的智能指针并将另一个智能指针的资源所有权转移给它时,移动构造不会影响引用计数。引用计数会在资源所有权转移后保持不变,原来的智能指针会被置为 nullptr ,不再指向资源。这确保资源的唯一所有权。

1
2
3
4
5
6
7
8
9
10
new_shared_ptr(new_shared_ptr&& dying_obj)
: ptr(nullptr), ref_count(nullptr) {
// 初始化后,调用swap,交换指针和引用计数,相当于清除了原有的shared_ptr,把它们都置为nullptr了
dying_obj.swap(*this);
}

void swap(new_shared_ptr& other) {
std::swap(ptr, other.ptr);
std::swap(ref_count, other.ref_count);
}
移动赋值

当使用移动赋值运算符将一个智能指针的资源所有权转移给另一个智能指针时,移动赋值不会影响引用计数。与移动构造类似,资源的所有权转移后,原来的智能指针会被置为 nullptr ,引用计数保持不变。

1
2
3
4
5
6
new_shared_ptr& operator=(new_shared_ptr&& dying_obj) {
/* 创建一个临时变量,将待移动的智能指针转移到临时变量 */
new_shared_ptr(std::move(dying_obj)).swap(*this);
return *this;
}

析构函数

1
2
3
4
5
6
7
8
9
10
11
~new_shared_ptr() {
if (!ptr || (*ref_count == 1)) {
delete ptr;
delete m_pMutex;
delete ref_count;
} else {
m_pMutex->lock();
(*ref_count)--;
m_pMutex->unlock();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <iostream>
using namespace std;

// 示例用类:Type,仅包含一个整数成员变量
class Type
{
public:
int a = 1;
};

// 引用计数类,用于管理资源的引用数量
class share_count {
public:
// 构造函数,初始化引用计数为 1(表示首次拥有资源)
share_count() : _count(1) {}

// 增加引用计数
void add_count() {
++_count;
}

// 减少引用计数,并返回当前引用计数
long reduce_count() {
return --_count;
}

// 获取当前引用计数
long get_count() const {
return _count;
}

private:
long _count; // 当前引用计数
};

// 智能指针类模板,模仿 shared_ptr 实现
template<typename T>
class smart_ptr
{
public:
// 构造函数:接受一个原始指针参数
// 如果传入非空指针,则初始化引用计数对象
smart_ptr(T* ptr = NULL) : m_ptr(ptr), m_share_count(nullptr) {
if (ptr) {
m_share_count = new share_count;
}
}

// 析构函数:如果引用计数为 0,则释放资源
~smart_ptr() {
if (m_ptr && m_share_count && m_share_count->reduce_count() == 0) {
delete m_ptr; // 释放资源对象
delete m_share_count; // 释放引用计数对象
cout << "~smart_ptr" << endl; // 输出析构提示
}
}

// 解引用操作符:返回对象引用
T& operator*() const { return *m_ptr; }

// 成员访问操作符:返回原始指针
T* operator->() const { return m_ptr; }

// 布尔类型转换:用于判断指针是否为空
operator bool() const { return m_ptr != nullptr; }

// 拷贝构造函数:共享资源,增加引用计数
smart_ptr(const smart_ptr& rhs) noexcept {
m_ptr = rhs.m_ptr;
m_share_count = rhs.m_share_count;
if (m_ptr) {
m_share_count->add_count();
}
}

// 拷贝赋值运算符:先释放自身管理的资源,再共享 rhs 的资源
smart_ptr& operator=(const smart_ptr& rhs) noexcept {
if (this != &rhs) { // 避免自我赋值
// 如果当前持有的资源引用计数减为0,释放资源
if (m_ptr && m_share_count && m_share_count->reduce_count() == 0) {
delete m_ptr;
delete m_share_count;
}
// 共享新资源并增加引用计数
m_ptr = rhs.m_ptr;
m_share_count = rhs.m_share_count;
if (m_ptr) {
m_share_count->add_count();
}
}
return *this;
}

// 返回当前引用计数
long use_count() const {
if (m_ptr && m_share_count) {
return m_share_count->get_count();
}
return 0;
}

private:
T* m_ptr; // 原始指针,指向实际资源
share_count* m_share_count; // 引用计数对象
};

阅读全文
C# 的委托与事件

​ 这篇文章是我学习 C# 语言过程中对委托(delegate)和事件(event)的一些简单理解和记录。刚开始接触这两个概念时觉得有点抽象,不太明白它们和普通方法调用有什么区别,也搞不清它们之间的关系。

​ 后来通过查资料、写了一些小例子,慢慢有了一些初步的认识。这里整理一下我学到的内容,包括基本语法、调用过程、委托和事件的区别,以及我自己的理解,方便以后复习,也希望对其他正在学习的人有一点点参考价值

阅读全文
Sort Ⅰ

​ 几个排序算法

阅读全文
Huffman 编码 & Union-Find 并查集

​

哈夫曼编码

并查集

并查集的优化
阅读全文
Binary Tree

​ 树是个让我比较头疼的知识点,变化比较多样,各种性质也让我感到烦琐。二叉树就是一种树形数据结构,其中每个节点最多只能有两个子节点,分别称为左子节点和右子节点。

阅读全文
字符串模式匹配/KMP

​ 日常网路生活中很多地方都需要用到字符匹配,最常见的如使用搜索引擎的时候。

​ 这里我们学习两种算法:朴素模式匹配算法、KMP算法。

阅读全文
Queue 容器
STL | Container | C++

​ STL中的Queue容器是基于别的容器封装的,默认实现是基于deque封装实现了,但基于苯人的学习于实现顺序,我们基于List容器封装实现一个Queue容器。

阅读全文
零数组变换

​ 在力扣每日一题做到的一系列题目,初次碰到的时候觉得颇有意思,遂记录。

阅读全文