智能指针

智能指针

本节目标
  • 为什么需要智能指针?
  • 智能指针的使用及原理
  • C++11和boost中智能指针的关系
  • RAII扩展学习
为什么需要智能指针
  • 但是如果像下面这样,出现了一场情况,以及代码可能以前终止掉的情况,你都需要对开辟的内存空间以及打开的文件指针进行释放以及关闭的操作,那么其实重复的操作就干了太多次了
  • 那么如果有这样一个指针可以替我们管理起来这些情况的话,我们就不用再去担心这些问题的发生了,也就是说我们写代码的时候不用考虑的那么复杂了,同时代码写起来也是比较舒服的
#include<iostream>
using namespace std;

void TestFunc1()
{
	throw 1;
}

bool TestFunc2()
{
	return false;
}

//但是如果像下面这样,出现了一场情况,以及代码可能以前终止掉的情况
//你都需要对开辟的内存空间以及打开的文件指针进行释放以及关闭的操作
//那么其实重复的操作就干了太多次了
void TestFunc()
{
	int* p = new int[10];
	FILE* fp = fopen("666.txt", "rb");

	if (nullptr == fp)
	{
		delete[] p;
		return;
	}

	try
	{
		TestFunc1();
	}
	catch (...)
	{
		delete[] p;
		fclose(fp);
		return;
	}

	if (!TestFunc2())
	{
		delete[] p;
		fclose(fp);
		return;
	}

	delete[] p;
	fclose(fp);
}
int main()
{
	return 0;
}
  • 所以为什么需要给出智能指针呢—其实就是为了去管理指针,以及让其自动取释放资源
  • 那么,如何去实现智能指针呢?----其实实现智能指针的话,一定是需要用到RAII的
智能指针的使用及原理
RAII
  • RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术
  • 在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处—(1)不需要显式地释放资源。(2)采用这种方式,对象所需的资源在其生命期内始终保持有效
  • 那么,既然要实现自动去释放资源的这一个问题,那么我们就需要把指针交给一个类来进行管理
  • 代码如下所示:
  • 但是下面的这个代码,其实不能完全的看成是一个指针的操作,sp本事上其实还是一个对象,因为如果是指针的话,指针是可以进行解引用操作的,而sp是不可以进行解引用操作的,所以sp本质上其实还是一个对象
  • 虽然确实不用再考虑指针释放内存资源的问题,但是他也确实还不具有指针的行为
#include<iostream>
using namespace std;
template<class T>
class SmartPtr
{
public:
	SmartPtr(T* ptr=nullptr)  //如果用户没有提供的话,我将其给成一个空其实就ok
		:_ptr(ptr)
	{

	}

	~SmartPtr()
	{
		//我们首先需要去看一下这个指针到底有没有管理资源
		if (_ptr)
		{
			delete _ptr;
		}
	}

protected:
	T* _ptr;
};

void TestFunc()
{
	SmartPtr<int> sp(new int);  //现在就相当于是创建了一个SmartPtr类的对象sp
	//那么其实对象的空间的释放就不需要我们来再单独的进行处理了
	//因为再调用SmartPtr的析构函数的时候,是会替我们把对象的资源释放掉的
}

int main()
{
	TestFunc();

	return 0;
}
  • 那么如何让他具有指针的行为呢?
  • 其实只需要对*符号进行一下重载其实就是可以的了,同时也需要对->符号进行重载的操作
#include<iostream>
using namespace std;
template<class T>
class SmartPtr
{
public:
	SmartPtr(T* ptr = nullptr)  //如果用户没有提供的话,我将其给成一个空其实就ok
		:_ptr(ptr)
	{

	}

	~SmartPtr()
	{
		//我们首先需要去看一下这个指针到底有没有管理资源
		if (_ptr)
		{
			delete _ptr;
		}
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		//只需要把地址返回会去就可以了
		return _ptr;
	}

protected:
	T* _ptr;
};

void TestFunc()
{
	SmartPtr<int> sp(new int);  //现在就相当于是创建了一个SmartPtr类的对象sp
	//那么其实对象的空间的释放就不需要我们来再单独的进行处理了
	//因为再调用SmartPtr的析构函数的时候,是会替我们把对象的资源释放掉的
}

int main()
{
	TestFunc();

	return 0;
}
  • 重载完成之后,sp就具有了指针的特性,同时出了函数作用域之后,对象就会被销毁掉了
    在这里插入图片描述
    在这里插入图片描述
  • 但是其实上面的代码中,有一行语句其实是进行了一些优化的,因为本身sp2是一个对象,然后现在对->进行重载了之后,相当于是sp2->才相当于是一个指针的东西,但是如果你希望通过指针去访问一个变量的话,其实还是要再次加上->符号的,所以说这句话其实本身应该写成sp2->->a才对,但是现在省略了一个->符号,也就是说,编译器其实对这句话进行了一次优化的操作
    在这里插入图片描述
    在这里插入图片描述
  • RAII特性
  • 重载operator*和opertaor->,具有像指针一样的行为
auto_ptr
  • 如果没有显示给出拷贝构造函数的话,那么就会调用默认的拷贝构造函数,那么其实就会造成一个浅拷贝的风险
  • 那么再释放的时候,其实就会造成出错的问题
#include<iostream>
using namespace std;
namespace bite
{
	//首先,智能指针需要通过RAII的方式,然后再加上具有指针行为的操作
	//同时需要解决浅拷贝的问题
	template<class T>
	class auto_ptr
	{
	public:

		//首先是RAII的思想
		auto_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{

		}

		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}

		//然后去进行重载
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	protected:
		T* _ptr;
	};
}

void TestFunc()
{
	bite::auto_ptr<int> sp1(new int);

	//然后我现在希望用sp1去拷贝构造sp2
	bite::auto_ptr<int> sp2(sp1);
	//但是,现在我们又没有显示给出拷贝构造函数,所以用的就是浅拷贝的方式

}

int main()
{
	return 0;
}

在这里插入图片描述

  • 那么如何解决浅拷贝的问题呢—使用资源转移的方式解决浅拷贝的问题,这里之所以不能使用深拷贝来解决浅拷贝的问题是因为智能指针这里是不能自己来进行空间的申请的,空间是由外部用户所给出的,所以说在这里是不可以用深拷贝的方式来解决浅拷贝的问题的
  • 资源转移其实可以理解成,你现在希望利用sp1去拷贝构造出来sp2,那么也就是说sp2相比于sp1,他是一个更新的对象, 那么现在假设你用sp1拷贝构造出来sp2之后,你的sp1就不再使用了,那么也就是相当于把sp1中的资源转移给sp2来进行使用
#include<iostream>
using namespace std;
namespace bite
{
	//首先,智能指针需要通过RAII的方式,然后再加上具有指针行为的操作
	//同时需要解决浅拷贝的问题
	template<class T>
	class auto_ptr
	{
	public:

		//首先是RAII的思想
		auto_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{

		}

		auto_ptr(auto_ptr<T>& ap)
		{
			_ptr = ap._ptr;
			//因为ap里面的东西不再使用了,所以直接把ap的资源给成nullptr其实就是可以的了
			ap._ptr = nullptr;
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}

		//然后去进行重载
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	protected:
		T* _ptr;
	};
}

void TestFunc()
{
	bite::auto_ptr<int> sp1(new int);

	//然后我现在希望用sp1去拷贝构造sp2
	bite::auto_ptr<int> sp2(sp1);
	//但是,现在我们又没有显示给出拷贝构造函数,所以用的就是浅拷贝的方式

}

int main()
{
	return 0;
}
  • 同样的道理,赋值运算符重载也需要给出
#include<iostream>
using namespace std;
namespace bite
{
	//首先,智能指针需要通过RAII的方式,然后再加上具有指针行为的操作
	//同时需要解决浅拷贝的问题
	template<class T>
	class auto_ptr
	{
	public:

		//首先是RAII的思想
		auto_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{

		}

		auto_ptr(auto_ptr<T>& ap)
		{
			_ptr = ap._ptr;
			//因为ap里面的东西不再使用了,所以直接把ap的资源给成nullptr其实就是可以的了
			ap._ptr = nullptr;
		}

		auto_ptr<T>& operator=(auto_ptr<T>& sp)
		{
			if (this != &sp)
			{
				//如果即将要被赋值的东西他是有空间的, 那么你首先
				//需要把他的空间释放掉,不然就会造成代码崩溃的问题产生
				if(_ptr)
					delete _ptr;
				_ptr = sp._ptr;
				sp._ptr = nullptr;
			}
			return *this;
		}

		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
			}
		}

		//然后去进行重载
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}
	protected:
		T* _ptr;
	};
}

void TestFunc()
{
	bite::auto_ptr<int> sp1(new int);

	//然后我现在希望用sp1去拷贝构造sp2
	bite::auto_ptr<int> sp2(sp1);
	//但是,现在我们又没有显示给出拷贝构造函数,所以用的就是浅拷贝的方式

	sp1 = sp2;

}

int main()
{
	TestFunc();

	return 0;
}
auto_ptr的缺陷
  • 因为再auto_ptr中,是使用资源转移的方式来解决浅拷贝的问题的发生的,但是现在,你如果还希望对sp1去进行一些操作的话,其实就是不可以的,因为sp1的资源已经转移走了
  • 下面的代码其实就是对空指针进行了解引用的操作,那么代码是肯定会崩溃的
    在这里插入图片描述
    在这里插入图片描述
  • 那么发现了auto_ptr的问题之后,C++又对auto_ptr做出了改进,改进的方式其实就是给类中多添加了一个成员变量,因为最开始的版本是把资源直接转移出去了
#include<iostream>
using namespace std;
namespace bite
{
	//首先,智能指针需要通过RAII的方式,然后再加上具有指针行为的操作
	//同时需要解决浅拷贝的问题
	template<class T>
	class auto_ptr
	{
	public:

		//首先是RAII的思想
		auto_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_owner(false)   //之所以给成false是因为我现在并不
			//知道上面的指针到底是有效的还是无效的
		{
			if (_ptr)
				_owner = true;
		}

		auto_ptr(const auto_ptr<T>& ap)
			:_ptr(ap._ptr)
			,_owner(ap._owner)
		{
			ap._owner = false;
		}

		auto_ptr<T>& operator=(const auto_ptr<T>& ap)
		{
			if (this != &ap)
			{
				//如果即将要被赋值的东西他是有空间的, 那么你首先
				//需要把他的空间释放掉,不然就会造成代码崩溃的问题产生
				Release();
				_ptr = ap._ptr;
				_owner = ap._owner;
				ap._owner = false;
			}
			return *this;
		}

		~auto_ptr()
		{
			if (_ptr&&&_owner)
			{
				delete _ptr;
			}
		}

		//然后去进行重载
		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

	private:
		void Release()
		{
			if (_ptr && _owner)
			{
				delete _ptr;
			}
		}

	private:
		T* _ptr;
		mutable bool _owner;
	};
}

void TestFunc()
{
	bite::auto_ptr<int> ap1(new int);

	//然后我现在希望用sp1去拷贝构造sp2
	bite::auto_ptr<int> ap2(ap1);
	//但是,现在我们又没有显示给出拷贝构造函数,所以用的就是浅拷贝的方式

}

int main()
{
	TestFunc();

	return 0;
}
  • 所以auto_ptr会造成的问题其实就是会造成两个对象不能同时去访问东西,因为进行了资源转移,转移的那个对象成为了空指针,不能在继续再进行操作,另一个是会产生野指针的问题,一个资源释放了,剩下的两个的资源也被释放了
    在这里插入图片描述
    在这里插入图片描述
unique_ptr
  • unique的意思其实就是如果说资源要进行管理的话,只能将资源交给一个对象来进行管理
  • 一份资源只能让一个对象管理,不允许进行共享,也就是说unique_ptr是独立管理资源的,不共享资源,那么也就是不能进行拷贝的工作,C++98中为了防止进行拷贝,我们可以将拷贝构造函数于赋值运算符重载函数只声明不定义并且将权限给成私有,C++11中拷贝构造函数和赋值运算符重载函数后面加上一个=delete
#include<iostream>
#include<memory>

using namespace std;

namespace bite
{
	template<class T>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{

		}

		~unique_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
				_ptr=nullptr;
			}
		}

		T& operator*()
		{
			return *_ptr;
		}
			
		T* operator->()
		{
			return _ptr;
		}

		//现在我让其防止拷贝
		//C++98中所给出的解决方式其实就是
	//private:
		//unique_ptr(const unique_ptr<T>&);
	    //unique_ptr<T>& operator=(const unique_ptr<T>&);


	    //C++11
		unique_ptr(const unique_ptr<T>&)=delete;
		unique_ptr<T>& operator=(const unique_ptr<T>&)= delete;
	private:
		T* _ptr;
	};
}

int main()
{
	bite::unique_ptr<int> up1(new int);
	bite::unique_ptr<int> up2(up1);

	return 0;

}
  • 如果像上面那样写代码的话,代码其实就会发生报错了,报错的原因在于,尝试调用已经删除的函数
    在这里插入图片描述
  • 使用赋值的话,其实也是会发生报错的,报错的原因和上面一摸一样,尝试调用已经删除的函数
    在这里插入图片描述
unique_ptr的缺陷
  • 其实unique_ptr是有一定的缺陷的,他的缺陷其实就在于,对象和对象之间不能共享资源,那么如果你现在确实需要对象和对象之间共享资源的话,其实就是不能使用unique_ptr的
  • 但是其实上面的代码像上面那样写的话,其实还是有问题的,因为我们可能涉及到的资源有可能是多种多样的,比如说用new申请的空间,比如说用malloc申请的空间,再比如说是文件指针,所以我们在对对象进行释放操作的时候,其实是不可以写死的
#include<iostream>
#include<memory>
using namespace std;

template<class T>
class Delete
{
public:
	void operator()(T*& p)
	{
		if (p)
		{
			delete p;
			p = nullptr;
		}
	}
};

template<class T>
class Free
{
public:
	void operator()(T* &p)
	{
		if (p)
		{
			free(p);
			p = nullptr;
		}
	}
};

template<class T>
class FClose
{
public:
	void operator()(FILE* &p)
	{
		if (p)
		{
			fclose(p);
			p = nullptr;
		}
	}
};
namespace bite
{
	template<class T,class DF=Delete<T>>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{

		}

		~unique_ptr()
		{
			if (_ptr)
			{
				//delete _ptr;
				DF df;
				df(_ptr);
				_ptr = nullptr;
			}
		}

		T& operator*()
		{
			return *_ptr;
		}

		T* operator->()
		{
			return _ptr;
		}

		//C++11
		unique_ptr(const unique_ptr<T>&) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>&) = delete;
	private:
		T* _ptr;
	};
}

int main()
{
	//bite::unique_ptr<int> up1(new int);
	//bite::unique_ptr<int> up2(up1);

	bite::unique_ptr<int> up1(new int);
	bite::unique_ptr<int, Free<int>> up2((int*)malloc(sizeof(int)));
	bite::unique_ptr<FILE,FClose<FILE>> up3(fopen("1.txt","w"));


	return 0;

}
shared_ptr
  • C++11中开始提供更靠谱的并且支持拷贝的shared_ptr
  • 那么,如何使得资源知乎释放一次呢?—我们采取引用计数的方式,就可以实现共享了
    在这里插入图片描述
  • shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源
    在这里插入图片描述
  • 赋值运算符重载的话,是需要分情况来进行讨论的
    在这里插入图片描述
#include<iostream>
#include<memory>
using namespace std;


namespace bite
{
	template<class T>

	class shared_ptr
	{
	public:

		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_pCount(nullptr)
		{
			if (ptr)
				_pCount = new int(1);
		}

		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pCount(sp._pCount)
		{
			if(_ptr)
				++(*_pCount);
		}

		//赋值运算符的重载
		shared_ptr& operator=(const shared_ptr<T>& sp)
		{
			if (_ptr!=sp._ptr)
			{
				Release();

				//与sp所共享
				_ptr = sp._ptr;
				_pCount = sp._pCount;

				//sp的资源多了当前对象去共享
				if (_ptr)
				{
					++(*_pCount);
				}
			}
			return *this;
		}


		~shared_ptr()
		{
			Release();
		}

		T& operator*()
		{
			return *_ptr;
		}
			
		T* operator->()
		{
			return _ptr;
		}

		int usecount()
		{
			return *_pCount;
		}
	private:
		void Release()
		{
			if (_ptr && 0 == --(*_pCount))
			{
				delete _ptr;
				delete _pCount;
			}
		}
	private:
		T* _ptr;
		int* _pCount;
	};
}

void TestFunc()
{
	bite::shared_ptr<int> sp1(new int);
	bite::shared_ptr<int> sp2(sp1);

	bite::shared_ptr<int> sp3(new int);
	bite::shared_ptr<int> sp4(sp3);

	sp4 = sp1;


}

int main()
{
	TestFunc();
	return 0;
}
  • 但是,我们再shared_ptr中如果使用如下的方式来进行使用,那么会不会有什么问题出现呢?
    在这里插入图片描述
  • 所存在的问题,在于我们进行空间申请的时候,实际上是存在有多种申请方式的,但是我们再进行析构的时候,其实我们只给出了一种析构的方式
    在这里插入图片描述
  • 那么我们现在就需要对这种问题来进行解决,如何解决呢?就是对于指针的释放,是不能写死的,如果写死的话,那么空间的申请和释放其实就无法匹配上了,就会导致代码出现一定的问题,也就是说,需要根据资源的类型来决定资源的释放方式
  • 那么,我们采用仿函数的方式对问题来进行解决
#include<iostream>
#include<memory>
using namespace std;

template<class T>
class DFDel
{
public:
	void operator()(T* p)
	{
		delete p;
		p = nullptr;
	}
};

template<class T>
class Free
{
public:
	void operator()(T* p)
	{
		if (p)
		{
			free(p);
			p = nullptr;
		}
	}
};

class FClose
{
public:
	void operator()(FILE* p)
	{
		if (p)
		{
			fclose(p);
			p = nullptr;
		}
	}
};


namespace bite
{
	template<class T,class DF= DFDel<T>>

	class shared_ptr
	{
	public:

		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_pCount(nullptr)
		{
			if (ptr)
				_pCount = new int(1);
		}

		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pCount(sp._pCount)
		{
			if(_ptr)
				++(*_pCount);
		}

		//赋值运算符的重载
		shared_ptr& operator=(const shared_ptr<T>& sp)
		{
			if (this != &sp)
			{
				Release();

				//与sp所共享
				_ptr = sp._ptr;
				_pCount = sp._pCount;

				//sp的资源多了当前对象去共享
				if (_ptr)
				{
					++(*_pCount);
				}
			}
			return *this;
		}


		~shared_ptr()
		{
			Release();
		}

		T& operator*()
		{
			return *_ptr;
		}
			
		T* operator->()
		{
			return _ptr;
		}

		int usecount()
		{
			return *_pCount;
		}
	private:
		void Release()
		{
			if (_ptr && 0 == --(*_pCount))
			{
				//delete _ptr;

				//DF是一个类型
				//DF()是创建一个无名的对象
				DF()(_ptr);
				delete _pCount;
			}
		}
	private:
		T* _ptr;
		int* _pCount;
	};
}

void TestFunc()
{
	bite::shared_ptr<int,Free<int>> sp1((int*)malloc(sizeof(int)));
	bite::shared_ptr<FILE,FClose> sp2(fopen("666.txt","rb"));

	//下面的这种写法其实是不ok的,因为在C++11中的智能指针其实都是不能管理连续的空间的
	//原因在于因为在标准库里面其实已经有了一个可以管理连续空间的东西
	//其实就是vector
	//bite::shared_ptr<int> sp3(new int[10]);
}

int main()
{
	TestFunc();
	return 0;
}
shared_ptr的缺陷—可能会产生循环引用以及线程安全的问题

在这里插入图片描述

  • shared_ptr的缺陷—不是线程安全的,假如说,两个线程现在在底层用到了同一份的智能’指针,那么就可能会产生一种想象,其中一个线程进行到一半他的时间片的时间到了,现在他就不能再继续去进行了,然后第二个线程开始运行,他认为只有他一个线程在用这份资源,那么,当他结束的时候,他就会把资源释放掉,但是现在其实还有一个线程存在,所以说就会出现问题,那么我们就需要去进行加锁的操作
  • 但是其实加锁也不是最理想的解决方式,因为加锁会造成死锁的问题
#include<iostream>
#include<memory>
#include<mutex>
using namespace std;

template<class T>
class DFDel
{
public:
	void operator()(T* p)
	{
		delete p;
		p = nullptr;
	}
};

template<class T>
class Free
{
public:
	void operator()(T* p)
	{
		if (p)
		{
			free(p);
			p = nullptr;
		}
	}
};

class FClose
{
public:
	void operator()(FILE* p)
	{
		if (p)
		{
			fclose(p);
			p = nullptr;
		}
	}
};


namespace bite
{
	template<class T,class DF= DFDel<T>>

	class shared_ptr
	{
	public:

		shared_ptr(T* ptr = nullptr)
			:_ptr(ptr)
			,_pCount(nullptr)
			,_pMutex(new mutex)
		{
			if (ptr)
				_pCount = new int(1);
		}

		shared_ptr(const shared_ptr<T>& sp)
			:_ptr(sp._ptr)
			,_pCount(sp._pCount)
		{
			AddRefCount();
		}

		//赋值运算符的重载
		shared_ptr& operator=(const shared_ptr<T>& sp)
		{
			if (this != &sp)
			{
				Release();

				//与sp所共享
				_ptr = sp._ptr;
				_pCount = sp._pCount;

				//sp的资源多了当前对象去共享
				AddRefCount();
			}
			return *this;
		}


		~shared_ptr()
		{
			Release();
		}

		T& operator*()
		{
			return *_ptr;
		}
			
		T* operator->()
		{
			return _ptr;
		}

		int usecount()
		{
			return *_pCount;
		}
	private:
		void Release()
		{
			//上锁
			_pMutex->lock();
			if (_ptr && 0 == --(*_pCount))
			{
				//delete _ptr;

				//DF是一个类型
				//DF()是创建一个无名的对象
				DF()(_ptr);
				delete _pCount;
			}
			//解锁
			//如果离开的时候不进行解锁的话,那么就会造成死锁了
			_pMutex->unlock();
		}

		void AddRefCount()
		{
			_pMutex->lock();
			if (_ptr)
				++(*_pCount);
			_pMutex->unlock();

		}

	private:
		T* _ptr;
		int* _pCount;
		mutex* _pMutex;
	};
}

void TestFunc()
{
	bite::shared_ptr<int,Free<int>> sp1((int*)malloc(sizeof(int)));
	bite::shared_ptr<FILE,FClose> sp2(fopen("666.txt","rb"));

	//下面的这种写法其实是不ok的,因为在C++11中的智能指针其实都是不能管理连续的空间的
	//原因在于因为在标准库里面其实已经有了一个可以管理连续空间的东西
	//其实就是vector
	//bite::shared_ptr<int> sp3(new int[10]);
}

int main()
{
	TestFunc();
	return 0;
}
  • shared_ptr的缺陷—可能会产生循环引用的问题

  • 花括号里面再打印一句话,打印函数名称就行在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

改变

在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值