智能指针

    在介绍智能指针之前,我先来解释两个词:

          ①内存泄漏:由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete,比如流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见,并造成内存泄露。通俗的讲,是指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。

内存泄漏的成因就不说了,因为上述概念就是成因

          ②指针悬挂:悬挂指针也叫野指针,是未初始化或未清零的指针。

悬挂指针的成因主要有两种:

  • 指针变量没有被初始化 
    任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
  • 指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。 
    别看free和delete的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。

 

1.  为什么要有智能指针?

  • 就是为了解决内存泄漏指针悬挂问题的。如此c++引入 智能指针 ,智能指针即是C++ RAII的一种应用,可用于动态资源管理,资源即对象的管理策略。 智能指针在 <memory>标头文件的 std 命名空间中定义。RAII 的主要原则是为所有堆分配资源提供所有权,以及任何相关清理代码。

2.智能指针的类别:

  • c++ 智能指针主要包括:scope_ptr,shared_ptr, weak_ptr, 这三种,其中auto_ptr 已被遗弃,基本没人去使用。本着学习的原则,我接下来会逐一介绍四个指针。
  • auto_ptr的主要思想是,控制权转移。
  • scope_ptr(有的时候也称unique_ptr)的主要思想是,不允许拷贝,不允许赋值。
  • share_ptr的主要思想是,采用引用计数。但是它带来一个循环引用的问题。
  • weak_ptr是根share_ptr配套使用的,解决循环引用问题   

auto_ptr.h

#pragma once

template<class T>class Auto_ptr
{
public:
	//RAII的思想
	Auto_ptr(T *ptr)
		:_ptr(ptr)
	{}
	~Auto_ptr()
	{
		delete _ptr;//这个智能指针智能指向new出来的空间
		_ptr = NULL;
	}
	//像普通指针一样
	T&operator*()
	{
		return *_ptr;
	}
	T*operator->()
	{
		return _ptr;
	}
	//(思想一:管理权转移)处理多个指针指向同一块空间问题
	Auto_ptr(Auto_ptr<T>&ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = NULL;
	}
	Auto_ptr<T>&operator=(Auto_ptr<T>&ap)
	{
		if (this != &ap)
		{
			_ptr = ap._ptr;
			ap._ptr = NULL;
		}
		return *this;
	}
private:
	T* _ptr;
};

 

scope_ptr

#pragma once

template<class T>class Scoped_ptr
{
public:
	//RAII的思想
	Scoped_ptr(T*ptr)
		:_ptr(ptr)
	{}
	~Scoped_ptr()
	{
		delete _ptr;//这个智能指针智能指向new出来的空间
		_ptr = NULL;
	}
	//像普通指针一样
	T&operator*()
	{
		return *_ptr;
	}
	T*operator->()
	{
		return _ptr;
	}
	//(思想二:防拷贝,赋值)定义删除函数
	Scoped_ptr(const Scoped_ptr<T>&ap) = delete;
	Scoped_ptr<T>&operator=(const Scoped_ptr<T>&ap) = delete;
private:
	(思想二:防拷贝,赋值)在私有下,只声明不定义
	//Scoped_ptr(const Scoped_ptr<T>&ap);
	//Scoped_ptr<T>&operator=(const Scoped_ptr<T>&ap);

private:
	T* _ptr;
};

 

share_ptr

#pragma once

template<class T>class Weak_ptr;//前置声明

template<class T>class Shared
{
public:
	friend Weak_ptr<T>;
	//RAII的思想
	Shared(T *ptr = NULL)
		:_ptr(ptr)
		, _refcount(new int(1))
	{}
	~Shared()
	{
		if (--(*_refcount) == 0)
		{
			delete _ptr;
			delete _refcount;
		}
		_ptr = NULL;
		_refcount = NULL;
	}
	//像普通指针一样
	T&operator*()
	{
		return *_ptr;
	}
	T*operator->()
	{
		return _ptr;
	}
	Shared(Shared<T>& ap)
		:_ptr(ap._ptr)
		, _refcount(ap._refcount)
	{
		++(*_refcount);
	}
	Shared<T>&operator=(Shared<T>&ap)
	{
		if (_ptr != ap._ptr)//判断两个对象是否指向同一块空间(防止给自己赋值)
		{
			if (--(*_refcount) == 0)
			{            //①判断现在只想的空间是否还有其它指针指向②顺便把引用计数减一
				delete _ptr;
				delete _refcount;
				_ptr = NULL;
				_refcount = NULL;
			}
			_ptr = ap._ptr;
			_refcount = ap._refcount;
			++(*_refcount);
		}
		return *this;
	}
private:
	T *_ptr;
	int *_refcount;
};

 

weak_ptr

#pragma once

//Weak_ptr只能与shared_ptr一起配套使用,解决循环引用问题
template<class T>
class Weak_ptr
{
public:
	//RAII的思想
	Weak_ptr(const Shared<T>&sp = Shared<T>())
		:_ptr(sp._ptr)
	{}
	//像普通指针一样
	T&operator*()
	{
		return *_ptr;
	}
	T*operator->()
	{
		return _ptr;
	}
	Weak_ptr<T>&operator=(Shared<T>&ap)
	{
		_ptr = ap._ptr;
		return *this;
	}
private:
	T *_ptr;
};

 

main.cpp

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include<CoreWindow.h>

#include"auto_ptr.h"
#include"scped_ptr.h"
#include"shared_ptr.h"
#include"weak_ptr.h"
//所谓智能指针就是智能/自动化的管理指针所指向的动态资源的释放
//需要智能指针的目的:防止内存泄漏。

//智能指针的三大要素:①RAII思想(Resource Acquisition Is Initialization)
                       //资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源
                       //的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。
                    //②能像普通指针一样运算
                    //③解决多个智能指针指向同一块空间的问题,防止该空间被释放多次
class AA
{
public:
	int x;
};

void testAuto_ptr()
{
	int a = 5;

	Auto_ptr<int>ap1(new int(2));
	*ap1 = 10;
	Auto_ptr<AA> ap2 = new AA;
	ap2->x = 10;
	Auto_ptr<int>ap3(ap1);
	Auto_ptr<int>ap4(new int(2));
	ap4 = ap3;
	if (1)
	{
		return;
	}
}
void testScoped_ptr()
{
	Scoped_ptr<int>ap1(new int(2));
	*ap1 = 10;
	Scoped_ptr<AA> ap2 = new AA;
	ap2->x = 10;

	if (1)
	{
		return;
	}
}

void testShared_ptr()
{
	Shared<int>ap1(new int(2));
	*ap1 = 10;
	Shared<AA> ap2 = new AA;
	ap2->x = 10;
	Shared<int>ap3(ap1);
	Shared<int>ap4(new int(2));
	ap4 = ap3;
	if (1)
		return;

}
//上面三个测试函数,分别测了三个指针
//------------------------------------分界线---------------------------------------//
//下面的两个测试函数,测试循环引用问题的
struct node
{
	int _data;
	Shared<node> _next;
	node()
		:_data(int())
		, _next(Shared<node>())
	{}
	~node()
	{
		cout << "~node()" << endl;
	}
};
void test()
{
	Shared<node> next = new node;
	{
		Shared<node> cur = new node;
		cur->_next = next;
		next->_next = cur;//这句代码造成循环引用问题
	}
	cout << "hah";
}
struct node2
{
	int _data;
	Weak_ptr<node2> _next;
	node2()
		:_data(int())
		, _next(Weak_ptr<node2>())
	{}
	~node2()
	{
		cout << "~node2()" << endl;
	}
};
void test2()
{
	Shared<node2> next = new node2;
	{
		Shared<node2> cur = new node2;
		cur->_next = next;
		next->_next = cur;//这句代码造成循环引用问题
	}
	cout << "hah";
}



int main()
{
	//testAuto_ptr();
	//testScoped_ptr();
	//testShared_ptr();

	//test();
	//test2();

	system("pause");
	return 0;
}

 

3.下面一幅图是我对循环引用的理解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值