1,SmartPointer 智能指针重构
需求: 使用智能指针 SmartPointer 替换单链表 LinkList 中的原生指针
将原生指针更改为智能指针后, 解决全部的编译问题, 程序还是会出错, 问题在于: SmartPointer 的设计方案存在的一些特性
指针的生命周期结束时主动释放堆空间
一片堆空间最多只能有一个指针标识
不允许指针运算和指针比较
需求: 创建新的指针指针
Pointer 是智能指针的抽象父类 (模板)
纯虚析构函数
virtual ~Pointer() = 0
重载 operator ->()
重载 operator* ()
智能指针新的设计方案
- template <typename T>
- class Pointer : public Object
- {
- protected:
- T* m_pointer;
- public:
- Pointer(T* p = NULL)
- {
- m_pointer = p;
- }
- T* operator-> ()
- {
- return m_pointer;
- }
- T& operator* ()
- {
- return *m_pointer;
- }
- bool inNull()
- {
- return (m_pointer == NULL);
- }
- T* get()
- {
- return m_pointer;
- }
- // 只要没有实现一个具体的析构函数, Pointer 继承于 Object, 就是一个抽象类
- };
修改 SmartPointer, 继承于 Pointer 类
- #ifndef SMARTPOINTER_H
- #define SMARTPOINTER_H
- #include "Pointer.h"
- namespace DTLib
- {
- // 构建一个智能指针模板类
- template<typename T>
- class SmartPointer : public Pointer<T>
- {
- public:
- // 构造函数, 初始化传参为堆空间地址
- SmartPointer(T* p = NULL) : Pointer<T>(p) // 调用父类的构造函数的形式
- {
- // 构造函数调用父类的构造函数
- }
- // 拷贝构造函数
- SmartPointer(const SmartPointer<T>& obj)
- {
- this->m_pointer = obj.m_pointer;
- const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
- //
- }
- SmartPointer<T>& operator = (const SmartPointer<T>& obj)
- {
- if(this != &obj)
- {
- // 释放掉原来指向的那个堆空间
- // 如果先删除 m_pointer 指向的堆空间, 就有可能导致异常抛出
- // 要保证异常安全性
- // delete m_pointer;
- // 指向新的堆空间
- // m_pointer = obj.m_pointer;
- // 删除 obj 对象中 m_pointer 与这个堆空间的关联, 保证一个堆空间只有一个指针指向这个堆空间
- // const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
- // 为了异常安全性, 用一个临时变量保存 this->pointer 指针, 方便释放
- T* p = this->m_pointer;
- this->m_pointer = obj.m_pointer;
- const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
- delete p;
- }
- return *this;
- }
- // 析构函数需要冲重写, 否则就还是一个抽象类
- ~SmartPointer()
- {
- delete this->m_pointer;
- }
- };
- }
- #endif // SMARTPOINTER_H
2,SharedPointer 智能指针
需求: 多个智能指针指向同一片堆空间, 并且这些指针支持自动释放
SharedPointer 设计要点: 类模板
通过计数机制 ref 标识堆内存
堆内存被指向时: ref++
指针被置空时: ref--
ref == 0 时: 释放堆内存
计数机制原理剖析:
3 个指针同时指向了堆空间中的统一对象, 与对象相关联的计数标识应该是 3. 如果将 shared_pointer_3 置空, 应该计数减一, 计数变量为 2; 如果全部置空, 计数变量为 0, 意味着最后一个智能指针要将堆空间里面的对象销毁掉, 将堆空间的内存释放.
虚线矩形框将对象和计数变量框在了一起, 意味着每一个堆空间中的对象都和这个计数变量相关联, 这个计数变量也位于堆空间里面. 在具体实现上计数变量也是在堆空间里面创建的, 并且计数变量的生命周期和这个对象的生命周期是完全一致的.
SharedPointer 类的声明
- template <typename T>
- class SharedPointer : public Pointer<T>
- {
- protected:
- int* m_ref; // 计数机制成员指针
- // 成员指针指向堆空间里面创建的计数变量
- public:
- SharedPointer(T* p = NULL);
- SharedPointer(const SharedPointer<T>& obj);
- SharedPointer<T>& operator = (const SharedPointer<T>& obj);
- void clear(); // 当前指针置空
- ~SharedPointer();
- };
由于 SharedPointer 支持多个对象同时指向一片堆空间, 因此必须支持比较操作, 使智能指针最大限度上接近原生指针的逻辑.
具体实现如下:
- // SharedPointer.h
- #ifndef SHAREDPOINTER_H
- #define SHAREDPOINTER_H
- #include <cstdlib>
- #include "Exception.h"
- #include "Pointer.h"
- namespace DTLib
- {
- template <typename T>
- class SharedPointer : public Pointer<T>
- {
- protected:
- int* m_ref; // 计数机制成员指针
- // 进行函数封装
- void assign(const SharedPointer<T>& obj)
- {
- this->m_ref = obj.m_ref; // 将当前指针对象的 ref 成员指针指向了对应的计数对象
- this->m_pointer = obj.m_pointer; // m_pointer 指向对应的堆内存
- // 还不够, 注意计数机制, 计数变量需要 + 1
- if (this->m_ref)
- {// 计数变量合法
- (*this->m_ref)++;
- }
- }
- public:
- // 首先是构造函数对成员变量初始化
- SharedPointer(T* p = NULL) : m_ref(NULL) // 将指向计数变量的成员指针, 初始化为空
- {
- if (p)
- {
- // 首先在堆空间中创建一个计数变量
- // 在堆空间中申请 4 个字节空间作为存放计数变量的内存空间
- this->m_ref = static_cast<int*>(std::malloc(sizeof(int))); // malloc 返回类型是 void*
- // 判断申请是否成功
- if (this->m_ref)
- {
- *(this->m_ref) = 1; // 意味着参数指针 p 指向的堆空间已经有了一个 SharedPointer 智能指针对象来指向了
- this->m_pointer = p; // 将成员指针变量指向参数 p 对应的堆空间
- }
- else
- {// malloc 不成功, 意味着内存不够用, 抛异常
- THROW_EXCEPTION(NoEnoughMemoryException, "No memory to creat SharedPointer object...");
- }
- }
- }
- SharedPointer(const SharedPointer<T>& obj)
- {
- //this->m_ref = obj.m_ref; // 将当前指针对象的 ref 成员指针指向了对应的计数对象
- //this->m_pointer = obj.m_pointer; // m_pointer 指向对应的堆内存
- //// 还不够, 注意计数机制, 计数变量需要 + 1
- //if (this->m_ref)
- //{// 计数变量合法
- // (*this->m_ref)++;
- //}
- assign(obj);
- }
- // 赋值操作符重载函数
- SharedPointer<T>& operator = (const SharedPointer<T>& obj)
- {
- if (this != &obj)
- {// 避免自赋值
- // 逻辑与拷贝构造类似, 但是需要做清空操作
- // 当前的 SharedPointer 对象已经指向了另一片堆空间了, 在做赋值操作前, 应该将当前的智能指针对象置空, 不再指向任何堆空间
- // 在赋值之前, 置空 clear()
- clear();
- //// 可以代码复用, 封装内部函数
- //this->m_ref = obj.m_ref;
- //this->m_pointer = obj.m_pointer;
- //if (this->m_ref)
- //{
- // (*this->m_ref)++;
- //}
- assign(obj);
- }
- return *this;
- }
- void clear() // 当前指针置空
- {
- T* toDel = this->m_pointer;
- int* ref = this-> m_ref;
- this->m_pointer = NULL;
- this->m_ref = NULL;
- if (ref)
- {// 当前计数变量合法
- (*ref)--;
- if (*ref == 0)
- {// 为 0 标识该堆空间已经没有任何智能指针对象去指向了, 应该释放该堆空间
- free(ref); // 释放计数变量
- delete toDel; // 释放堆空间
- }
- }
- }
- ~SharedPointer()
- {
- clear();
- }
- };
- }
- #endif // SHAREDPOINTER_H
测试:
- int main()
- {
- SharedPointer<Test> sp0 = new Test();
- SharedPointer<Test> sp1 = sp0;
- SharedPointer<Test> sp2 = NULL;
- sp2 = sp1;
- sp2->value = 100;
- cout <<sp0->value <<endl;
- cout << sp1->value <<endl;
- cout << sp2->value <<endl;
- cout << (sp0 == sp2) << endl;
- return 0;
- }
结果:
- Test()
- 100
- 100
- 100
- 0
- ~Test()
sp0 sp1 sp2 均指向了同一片堆空间, 通过 sp2->value 更改值之后, 和原生指针效果一样, 但是进行指针比较的时候, 需要重载比较操作符
- #ifndef SHAREDPOINTER_H
- #define SHAREDPOINTER_H
- #include <cstdlib>
- #include "Exception.h"
- #include "Pointer.h"
- namespace DTLib
- {
- template <typename T>
- class SharedPointer : public Pointer<T>
- {
- ...
- };
- // 在全局区重载比较操作符
- template <typename T>
- bool operator == (const SharedPointer<T>& l, const SharedPointer<T>& r)
- {
- return (l.get() == r.get());
- // get() 函数不是 const 成员函数, 所以不能被 const 对象调用
- }
- template <typename T>
- bool operator != (const SharedPointer<T>& l, const SharedPointer<T>& r)
- {
- return !(l == r); // != 操作符重载的实现用上面 == 操作的实现就可以了
- }
- }
- #endif // SHAREDPOINTER_H
智能指针的使用规则:
只能用来指向堆空间中的某个变量 (对象)
不同类型的智能指针对象不能混合使用
不用使用 delete 释放智能指针指向的堆空间
3, 小结
SharedPointer 最大程度地模拟了原生指针的行为
计数机制确保多个智能指针合法地指向同一片堆空间
智能指针只能用于指向堆空间中的内存
不同类型的智能指针不要混合使用
堆对象的生命周期由智能指针进行管理
来源: https://www.cnblogs.com/chenke1731/p/9655618.html