【C++】【原创】从0开始实现智能指针

实现简易智能指针

#include<iostream>
using namespace std;

// 智能指针 保证能做到资源的自动释放
// 智能指针实际利用栈上的对象出作用域自动析构的特征,来做到资源的自动释放
// 因为裸指针是个堆,所以需要手动释放对象,现在写成类后,就可以用栈来自动释放对象了
template<typename T>
class CSmartPtr
{
public:
	CSmartPtr(T *ptr = nullptr)
		:mptr(ptr) {}
	~CSmartPtr() { delete mptr; }

	T& operator*() { return *mptr; } //必须是T&,否则不能修改mptr指向的对象的值,反而是会创建一个临时副本,把值传给临时副本,而不是原对象
	T* operator->() { return mptr; }
private:
	T *mptr;
};

int main()
{
	CSmartPtr<int> ptrl(new int);
	*ptrl = 20;// 这一行使用了重载的解引用运算符来访问 ptrl 所管理的 int 对象,并将其值设置为 20。

	class Test
	{
	public:
		void test() { cout << "call Test::test" << endl; }
	};
	CSmartPtr<Test> ptr2(new Test());
	// (ptr2->operator->())->test();
	ptr2->test();
	
	return 0;
}

// 智能指针放在堆上
// CSmartPtr<int> *p = new CSmartPtr<int>(new int); delete p;这时候p是指向堆的智能指针,所以也要手动释放,这样就没啥意义了,还不如直接使用裸指针
// 在这段代码中,p 是一个指向 CSmartPtr<int> 类型的指针。*p 是一个 CSmartPtr<int> 类型的对象。
// 在这里,new int 是一个表达式,它使用 new 运算符动态分配内存来存储一个 int 类型的对象,并返回一个指向该对象的指针。这个指针被传递给 CSmartPtr<int> 的构造函数作为参数

问题

我们继续看下面的代码

CSmartPtr<int> p1(new int);
CSmartPtr<int> p2(p1);

这样的话,是会报错的,因为这会造成一个浅拷贝的问题(等以后再详细解释

那么为了解决浅拷贝的问题,我们用不带引用计数的智能指针和带引用计数的智能指针来解决

不带引用计数的智能指针

不带引用计数的:是只能一个指针管理资源

auto_ptr:C++库里面的

C++11新标准:scoped_ptr和unique_ptr

首先说auto_ptr

auto_ptr<int> p1(new int);
auto_ptr<int> p2(p1);

这样是可以正确运行的

但我们继续

auto_ptr<int> p1(new int);
auto_ptr<int> p2(p1);
*p2 = 20;
cout << *p1 << endl;

这是会报错的,那为什么呢,p1和p2不都是指向同一块内存吗

这时候咱们可以看下auto_ptr的拷贝构造函数看看了

auto_ptr(auto_ptr& _Right) noexcept : _Myptr(_Right.release()) {}
_Ty* release() noexcept {
        _Ty* _Tmp = _Myptr;
        _Myptr    = nullptr;
        return _Tmp;
    }

_Right就是咱们传入的p1了,p1调用release后,返回值初始化p2,在源代码里其实就是_Myptr,_Myptr查看源码,它是成员变量,也就是auto_ptr封装的裸指针

private:
    _Ty* _Myptr; 

其实auto_ptr在里面是这样走的,首先是把当前指针p1咱们的值先记下来,然后把当前指针p1改成nullptr,最后再把原来的值返回回去给p2

你把上面源代码release中的_Myptr换成p1就好理解了

但这样就带来了一个问题,那就是auto_ptr每次都是只记住最后一个指针,前面的指针都为空了

但我们的本意是想让p1和p2都可以访问到这个地址,所以这样说的话auto_ptr是有些问题的,所以你也能看到一般也都是不推荐使用auto_ptr

经常会被问到能不能再容器当中使用auto_ptr,其实尽量是别搞

vector<auto_ptr<int>> vec1;
vec2(vec1);

因为容器在使用过程中,难免会用到容器的拷贝构造或者容器的赋值,而这样的话,会影响容器内每个元素的拷贝赋值

当你用vec1构造vec2的时候,那就说明vec1里面的指针,全部为空,当你在不知道这个的情况下,你使用vec1里面的智能指针,就全部都是空指针了

既然auto_ptr尽量不用,那scoped_ptr呢

我们先看scoped_ptr的拷贝构造函数和拷贝赋值运算符

scoped_ptr(const scoped_ptr<T>&) = delete;
scoped_ptr<T>& operator=(const scoped_ptr<T>&) = delete;  

这些语句定义了scoped_ptr的拷贝构造函数和拷贝赋值运算符,它们使用了C++11中的关键字来禁用了这些函数

这意味着你不能使用拷贝构造函数或拷贝赋值运算符来创建一个 scoped_ptr对象的副本,如果你尝试这样做,编译器将报错

其实scoped_ptr的拷贝构造函数是被声明为private并且是没被定义的,这意味着你不能使用拷贝构造函数来创建一个 scoped_ptr对象的副本

这是为了防止多个 scoped_ptr对象管理同一个资源,从而避免在其中一个 scoped_ptr对象销毁时释放资源,导致其他 scoped_ptr 对象悬空

所以 scoped_ptr就不能这样写,这样写就是错的

scoped_ptr<int> p1(new int);
scoped_ptr<int> p2(p1);  

那该怎么办呢,也就只剩下unique_ptr了

我们看它的拷贝构造函数和拷贝赋值运算符

unique_ptr(const unique_ptr<T>&) = delete;
unique_ptr<T>& operator=(const unique_ptr<T>&) = delete;  

和上面scoped_ptr是一样的,那说明我们这样写也是错的

unique_ptr<int> p1(new int);
unique_ptr<int> p2(p1);  

但是如果这样写呢

uniq
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值