• C++11智能指针unique_ptr剖析


    unique_ptr介绍

    unique_ptr实现了独享被管理对象指针的概念,这意味着它可以确保一个对象和其对应的资源同一时间只被一个unique_ptr对象拥有。一旦拥有者被销毁或者变成empty,或者开始管理另一个对象先前拥有的对象就会被销毁,相应资源被回收

    unique_ptr特点:

    • delete: 左值引用拷贝构造和赋值
    • 提供 :右值引用拷贝构造或者赋值运算符
    • 右值引用拷贝构造或者赋值时,会release前一个对象的资源,保证只有一个unique_ptr指向资源
    template <class T>
    class My_unique_ptr {
    public:
    	using elem_type = T;
    	using elem_ptr = T*;
    
    	// 不允许进行隐式类型转换,只能用构造函数的显式写法
    	explicit My_unique_ptr(elem_ptr p)
    		:m_ptr_(p)
    	{}
    
    	~My_unique_ptr() {
    		delete m_ptr_;
    	}
    	
    	My_unique_ptr(const My_unique_ptr&) = delete;
    	My_unique_ptr& operator=(const My_unique_ptr&) = delete;
    
    private:
    	elem_ptr m_ptr_;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    辅助函数

    • get:返回指向被管理对象的指针
    • reset:替换被管理的对象,释放原来管理的资源,管理新的资源
    • release:释放被管理对象的所有权,即解除智能指针和资源的管理关系
    • swap:交换被管理对象的所有权
    • operator bool() const:检查是否有关联的被管理对象
    • 重载operator*,operator->,访问被管理的对象
    // get:返回指向被管理对象的指针
    elemPtr get() const {
    	return m_ptr_;
    }
    
    // reset:替换被管理的对象
    void reset(elemPtr p = nullptr) {
    	if (m_ptr_ != nullptr) {
    		delete m_ptr_;
    	}
    	m_ptr_ = p;
    }
    
    //  release:释放被管理对象的所有权,即解除智能指针和资源的管理关系
    elemPtr release() {
    	elemPtr tmp = m_ptr_;
    	e_ptr_ = nullptr;
    	return tmp;
    }
    
    // swap:交换被管理对象的所有权
    void swap(My_unique_ptr& other) {
    	std::swap(m_ptr_, other.m_ptr_);
    }
    	
    // operator bool() const:检查是否有关联的被管理对象
    explicit operator bool() const {
    	return m_ptr_ != nullptr;
    }
    
    //  重载operator * ,operator->,访问被管理的对象
    elemType operator*() const{
    	return *m_ptr_;
    }
    
    elemPtr operator->() const {
    	return m_ptr_;
    }
    
    • 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

    右值引用参数的拷贝构造和赋值

    // 这里不能像左值引用的形参一样,使用const,因为我们需要把形参引用的对象资源转移到当前对象,如果是const,则无法修改形参管理的对象
    My_unique_ptr(My_unique_ptr&& right) {
    	m_ptr_ = right.m_ptr_;
    	right.m_ptr_ = nullptr;
    }
    
    /*
    My_unique_ptr& operator=(My_unique_ptr&& right) {
    	if (this != &right) {
    		delete m_ptr_;          // 先释放原有资源
    		m_ptr_ = right.m_ptr_;
    		right.m_ptr_ = nullptr;
    	}
    	return *this;
    }
    */
    
    My_unique_ptr& operator=(My_uniqur_ptr&& right){
    	if(right != &right){
    		reset(right.release());
    	}
    	return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    测试代码

    int main(){
    	My_unique_ptr<Int> p1(new Int(10));
    	
    	// if(p1.operator bool())
    	if (p1) {
    		p1->show_value();
    	}
    	My_unique_ptr<Int> p2(std::move(p1));  // p2管理Int(10),p1为空
    	if (p1) {
    		p1->show_value();
    	}
    	if (p2) {
    		p2->show_value();
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    管理对象数组

    我们上面写的My_unique_ptr只能管理单个对象,而不能管理对象数组。我们需要提供两个删除器,一个用于删除单个对象,一个用于删除对象数组

    template<class T>
    struct my_default_deleter{
    	void operator()(T* ptr) const {
    		delete ptr;
    	}
    };
    
    template<class T>
    struct my_default_deleter <T[]> {
    	void operator()(T* ptr) const {
    		delete[] ptr;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    My_unique_ptr也需要提供两个版本,一个管理单个对象,一个用于管理对象数组

    template <class T, class Dx = my_default_deleter<T>>
    class My_unique_ptr {
    public:
    	using elemType = T;
    	using elemPtr = T*;
    	using deleterType = Dx;
    
    	// 不允许进行隐式类型转换,只能用构造函数的显式写法
    	explicit My_unique_ptr(elemPtr p = nullptr)
    		:m_ptr_(p)
    	{}
    
    	~My_unique_ptr() {
    		// delete m_ptr_;
    		reset();
    	}
    
    	// 这里不能像左值引用的形参一样,使用const,因为我们需要把形参引用的对象资源转移到当前对象,如果是const,则无法转移
    	My_unique_ptr(My_unique_ptr&& right) {
    		m_ptr_ = right.m_ptr_;
    		right.m_ptr_ = nullptr;
    	}
    
    	My_unique_ptr& operator=(My_unique_ptr&& right) {
    		if (this != &right) {
    			reset(right.release());
    		}
    		return *this;
    	}
    	
    	My_unique_ptr(const My_unique_ptr&) = delete;
    	My_unique_ptr& operator=(const My_unique_ptr&) = delete;
    
    	// get:返回指向被管理对象的指针
    	elemPtr get() const {
    		return m_ptr_;
    	}
    	deleterType& getDeleter() {
    		return m_deleter_;
    	}
    
    	const deleterType& getDeleter() const {
    		return m_deleter_;
    	}
    
    	// reset:替换被管理的对象
    	void reset(elemPtr p = nullptr) {
    		if (m_ptr_ != nullptr) {
    			// delete m_ptr_;
    			m_deleter_(m_ptr_);   // m_deleter.operator()(m_ptr_)
    		}
    		m_ptr_ = p;
    	}
    
    	//  release:释放被管理对象的所有权,即解除智能指针和资源的管理关系
    	elemPtr release() {
    		elemPtr tmp = m_ptr_;
    		m_ptr_ = nullptr;
    		return tmp;
    	}
    
    	// swap:交换被管理对象的所有权
    	void swap(My_unique_ptr& other) {
    		std::swap(m_ptr_, other.m_ptr_);
    		std::swap(m_deleter_, other.m_deleter_);
    	}
    
    	// operator bool() const:检查是否有关联的被管理对象
    	explicit operator bool() const {
    		return m_ptr_ != nullptr;
    	}
    
    	//  重载operator * ,operator->,访问被管理的对象
    	elemType operator*() const{
    		return *m_ptr_;
    	}
    
    	elemPtr operator->() const {
    		return m_ptr_;
    	}
    
    private:
    	elemPtr m_ptr_;
    	deleterType m_deleter_;
    };
    
    
    template <class T, class Dx>
    class My_unique_ptr<T[], Dx> {
    public:
    	using elemType = T;
    	using elemPtr = T*;
    	using deleterType = Dx;
    
    	// 不允许进行隐式类型转换,只能用构造函数的显式写法
    	explicit My_unique_ptr(elemPtr p = nullptr)
    		:m_ptr_(p)
    	{}
    
    	~My_unique_ptr() {
    		// delete m_ptr_;
    		reset();
    	}
    
    	// 这里不能像左值引用的形参一样,使用const,因为我们需要把形参引用的对象资源转移到当前对象,如果是const,则无法转移
    	My_unique_ptr(My_unique_ptr&& right) {
    		m_ptr_ = right.m_ptr_;
    		right.m_ptr_ = nullptr;
    	}
    
    	My_unique_ptr& operator=(My_unique_ptr&& right) {
    		if (this != &right) {
    			reset(right.release());
    		}
    		return *this;
    	}
    
    	My_unique_ptr(const My_unique_ptr&) = delete;
    	My_unique_ptr& operator=(const My_unique_ptr&) = delete;
    
    	// get:返回指向被管理对象的指针
    	elemPtr get() const {
    		return m_ptr_;
    	}
    	deleterType& getDeleter() {
    		return m_deleter_;
    	}
    
    	const deleterType& getDeleter() const {
    		return m_deleter_;
    	}
    
    	// reset:替换被管理的对象
    	void reset(elemPtr p = nullptr) {
    		if (m_ptr_ != nullptr) {
    			// delete m_ptr_;
    			m_deleter_(m_ptr_);   // m_deleter.operator()(m_ptr_)
    		}
    		m_ptr_ = p;
    	}
    
    	//  release:释放被管理对象的所有权,即解除智能指针和资源的管理关系
    	elemPtr release() {
    		elemPtr tmp = m_ptr_;
    		m_ptr_ = nullptr;
    		return tmp;
    	}
    
    	// swap:交换被管理对象的所有权
    	void swap(My_unique_ptr& other) {
    		std::swap(m_ptr_, other.m_ptr_);
    		std::swap(m_deleter_, other.m_deleter_);
    	}
    
    	// operator bool() const:检查是否有关联的被管理对象
    	explicit operator bool() const {
    		return m_ptr_ != nullptr;
    	}
    
    	elemType operator*() = delete;
    	elemPtr operator->() = delete;
    
    	elemType& operator[](std::size_t i) const {
    		return m_ptr_[i];
    	}
    
    private:
    	elemPtr m_ptr_;
    	deleterType m_deleter_;
    };
    
    • 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
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    int main(){
    	My_unique_ptr<Int[]> p1(new Int[3]);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    在这里插入图片描述

    用My_unique_ptr管理数组的时候,会使用使用数组模板类型的My_unique_ptr,并找到默认删除器,还会把当前数组类型加入到删除器的模板参数,使用删除数组的删除器

    管理非heap区资源

    添加文件删除器对象

    template <>
    struct my_default_deleter <FILE> {
    	void operator()(FILE* fp) const {
    		fclose(fp);
    	}
    };
    
    int main() {
    	My_unique_ptr<FILE> fp(fopen("1.txt", "w"));
    	if (!fp) {
    		exit(1);
    	}
    	fprintf(fp.get(), "%d %d %d", 1, 2, 3);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    或者添加带有删除器对象的构造函数

    My_unique_ptr(elemPtr p, const Dx& deleter)
    	: m_ptr_(p)
    	, m_deleter_(std::move(deleter))
    {}
    
    // 不写template<> 编译不过,删除器对象需要和My_unique_ptr底层的m_deleter_对象的类型相同
    template <>
    struct my_default_deleter <FILE> {
    	void operator()(FILE* fp) const {
    		fclose(fp);
    	}
    };
    
    
    int main() {
    	My_unique_ptr<FILE> fp(fopen("1.txt", "w"), my_default_deleter <FILE>());
    	if (!fp) {
    		exit(1);
    	}
    	fprintf(fp.get(), "%d %d %d", 1, 2, 3);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    使用unique_ptr总结

    unique_ptr在表达“专属所有权”的语义时使用,即unique_ptr永远拥有其指向的对象,所以unique_ptr是一个move-only类型。unique_ptr指针无法复制,只能将所有权在两个unique_ptr之间转移,转移后原来的unique_ptr被置为NULL

    • 语义简单,即当确定指针管理的对象不能被共享时,使用unique_ptr
    • unique_ptr效率比shared_ptr高,无需维护引用计数控制块
    • 在需要时,可以将unique_ptr转换成shared_ptr

    资源会在多个unique_ptr之间转移,最终最后的unique_ptr管理资源,其他的unique_ptr都为NULL

    My_unique_ptr<Int> fun(int a) {
    	My_unique_ptr<Int> pa(new Int(a));
    	return pa;
    }
    
    int main() {
    	My_unique_ptr<Int> p = fun(1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    Int(int value)                                   // 构造Int对象
    My_unique_ptr(elemPtr p = nullptr)               // 构造pa
    My_unique_ptr(My_unique_ptr&& right)             // 直接构造p,unique_ptr调用右值引用参数的拷贝构造,原来的指针为nullptr
    ~My_unique_ptr()                                 // 析构pa
    ~My_unique_ptr()                                 // 析构p
    ~Int()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    智能指针是比裸指针更智能的类,解决悬挂指针、多次释放空间、内存泄露等问题,通常用来确保指针的寿命和其他指向对象的寿命一致

  • 相关阅读:
    开源 Wiki 软件 wiki.js
    选择探针台时需要注意哪些事项
    一个注解搞定责任链,学还是不学?
    Twibot-22数据集介绍
    HarmonyOS4.0入门学习需要学习哪些知识点呢?
    无法将“node.exe”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。
    数据管理70个名词解析
    TypeScript基础内容(1)
    社区通过!Interim Grant Program提案详细内容(包含申请Grants链接)
    一串单词翻转——正则限制
  • 原文地址:https://blog.csdn.net/qq_42500831/article/details/126699499