C++[11] 智能指针

文章目录

       


  • 存在内存泄漏

        

int div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}
void Func()
{
	int* p1 = new int;//p1抛异常,直接跳catch,没有问题
	cout << div() << endl; // div抛异常,调到catch,p1无法释放,资源泄露
	delete p1;
}
int main()
{
	try
	{
		Func();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}

	return 0;
}

new空间也有可能会抛出异常,对于p1如果抛出异常:没有问题,可以不管,直接到最外面去了。

而如果用户输入的除数为0,那么div函数就会抛出异常,跳到主函数的catch块中执行,但是别忘了,此时Func()中的申请的内存资源还没有释放!

对于这种情况,我们可以进行异常的重新捕获

  • 异常的重新捕获

        在func函数中对div函数中的抛出的异常重新捕获,将申请的内存进行释放,然后在将异常重新捕获:

int div()
{
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");
	return a / b;
}
void Func()
{
	int* p1 = new int;//p1抛异常,直接跳catch,没有问题
	try
	{
		cout << div() << endl;
	}
	catch (...)
	{
		delete p1;
		throw;
	}
	delete p1;
}
int main()
{
	try
	{
		Func();
	}
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

但是如果申请的不是上面的一块空间,而是更多呢,还有p2,p3…?这时候就麻烦了,需要套很多,所以这时候智能指针就登场了,可以解决这个问题。

二、智能指针的使用与原理

  • 智能指针的使用
    template<class T>
    class SmartPtr
    {
    public:
    	SmartPtr(T* ptr)
    		:_ptr(ptr)
    	{}
    	~SmartPtr()
    	{
    		cout << "delete: " << _ptr << endl;
    		delete _ptr;
    	}
    	T& operator*()
    	{
    		return *_ptr;
    	}
    	T* operator->()
    	{
    		return _ptr;
    	}
    	T& operator[](size_t pos)
    	{
    		return _ptr[pos];
    	}
    private:
    	T* _ptr;
    };
    int div()
    {
    	int a, b;
    	cin >> a >> b;
    	if (b == 0)
    		throw invalid_argument("除0错误");
    	return a / b;
    }
    void func()
    {
    	SmartPtr<int> sp(new int);
    	int* p2 = new int;
    	SmartPtr<int> sp2(p2);
    
    	cout << div() << endl;
    }
    int main()
    {
    	try
    	{
    		func();
    	}
    	catch (exception& e)
    	{
    		cout << e.what() << endl;
    	}
    	return 0;
    }
    

    上面代码将申请到的内存交给了SmartPtr对象管理:

  • 在构造SmartPtr对象时,自动调用构造函数,将传入的需要管理的内存保存起来;

    在析构SmartPtr对象时,自动调用析构函数,将管理的内存空间进行释放

    SmartPtr还可以与普通指针一样使用,需对*和->以及[]进行运算符重载

通过SmartPtr对象,无论程序是正常执行结束,还是因为某些中途原因进行返回,或者抛出异常等开始所面临的困境,只要SmartPtr对象的生命周期结束就会自动调用对应的析构函数,不会造成内存泄漏,完成资源释放。 

  • 智能指针的原理

RAII: RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、互斥量等等)的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保存有效,最后在对象析构时释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象,好处:

不需要显式地释放资源

采用这种方式,对象所需的资源在其生命周期内始终保持有效

  • 智能指针对象拷贝问题

对于我们上面所实现的SmartPtr,如果用一个SmartPtr对象来拷贝构造另一个SmartPtr对象,或者一个SmartPtr对象赋值给另一个SmartPtr对象,最终结果会导致程序崩溃:

void test()
{
	SmartPtr<int> sp1(new int);
	SmartPtr<int> sp2(sp1);//拷贝构造
	SmartPtr<int> sp3(new int);
	SmartPtr<int> sp4 = sp3;//赋值
}

编译器默认生成的拷贝构造函数对内置类型完成浅拷贝(值拷贝),sp1拷贝给sp2后,两个管理同一块空间,当sp1和sp2析构就会让这块内存空间释放两次。同理,编译器默认生成的卡搜被赋值函数对内置类型完成浅拷贝,把sp4赋值给sp3后,sp4与sp3都是管理原来sp3的空间,会析构两次,同时,原先sp4管理的内存没有释放。

单纯的浅拷贝会导致空间多次释放,因为根据智能指针解决卡搜被问题方式不同,所以有很多版本的智能指针

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值