Advanced C ++ coolie road (smart pointers) XXIII

After the C ++ memory management (application to use the new space) to the users to manage their own, it is likely to apply for space forget released, causing a memory leak.
For example: there is no need to consider smart pointer, then memory leaks too many places, code is too bloated

#include<iostream>
using namespace std;
bool Test1()
{
	//成功返回true,失败返回false
	return false;
}
void Test2()
{
	//....

	throw 1; //抛出异常
}
void TestFunc()
{
	int *p = new int[10];
	FILE* fd = fopen("aaa.txt", "rb");
	if (fd == nullptr)
	{
		delete[] p;
		return;
	}
	if (!Test1())
	{
		delete[] p;
		fclose(fd);
		return;
	}
	try
	{
		Test2();
	}
	catch (...)
	{
		delete[] p;
		fclose(fd);
		throw;
	}
	//...
	
	//假如上面执行顺利,来到代码的末端
	delete[] p;
	fclose(fd);
}
int main()
{
	TestFunc();
	return 0;
}

The original ecology of pointers: T *
if the original ecological pointer to manage resources, then there is a risk of leakage of resources is relatively large program
to use the original eco-pointer prone to memory leaks, can make the program self-aware, so that objects in the destruction of their own time to go free up space.

RAII (resource acquisition and initialization) '
simple technique RAII (Resource Acquisition Is Initialization) is a use of an object to control the life cycle of the application resource
clever use of the constructor and destructor class.

//RAII --- 资源获取及初始化
//在构造函数中放资源
//在析构函数中释放资源

//智能指针的简单模拟实现
template<class T>
class SmartPtr
{
public:
	SmartPtr(T* ptr = nullptr)
	:_ptr(ptr)
	{
		cout << "SmartPtr" << endl;
	}
	~SmartPtr()
	{
		cout << "~SmartPtr" << endl;
		if (_ptr)
		{
			delete _ptr;
			_ptr = nullptr;  //加不加这句都可以,因为析构结束,对象已经销毁,成员变量也就不存在了
		}
	}
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
private:
	T* _ptr;
};
struct A
{
	int a = 100;
	int b = 200;
	int c = 300;
};
int main()
{
	SmartPtr<int> sp(new int);
	*sp = 10;
	cout << *sp << endl;
	SmartPtr<A> sp1(new A);
	cout << sp1->a << endl;;
	return 0;
}

RAII: users do not have to consider when to release resources, the timing of the release of resources to the compilers
defects existing code above:
the problem leads to a shallow copy of the previous string - "We are here to solve through a deep copy of
it in the smart pointer but we can not copy the way to solve the deep.
The reason:
Because only appear pointer just to solve the user's problem space forget release, users in the use of smart pointers do not know intelligent internal pointers, so the time can not achieve smart pointers with deep copy, because it is user case opened under and knowledge of the space, adding a user should not have more space, smart pointers interior space applications make the user feel confused, word is that the smart pointer inside a deep copy can be used to solve the problem but shallow copy because smart pointers just manage space, it is not possible to open up space.
All the problems of different types of smart pointers that must be addressed :()

  • RAII: resources can be released automatically
  • Class object pointer having a similar behavior: operator * operator->
  • Shallow copy solution

C ++ 98 in the thought of a way: auto_ptr
implementation:

namespace vs
{
	template<class T>
	class auto_ptr
	{
	public:
		auto_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{
		}
		~auto_ptr()
		{
			if (_ptr)
			{
				delete _ptr;
				_ptr = nullptr;
			}
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		auto_ptr(auto_ptr<T>& ap)  //这也就是auto_ptr解决浅拷贝的方式也就是资源的转移
			:_ptr(ap._ptr)
		{
			ap._ptr = nullptr;   //让原对象指向空
		}
		auto_ptr<T>& operator=(auto_ptr<T>& ps)
		{
			if (this != &ps)
			{
				if (!this)   //如果当前对象已经管理资源了,就要先把当前对象的资源先释放
					delete _ptr;
				_ptr = ps._ptr;
				ps._ptr = nullptr;
			}
			return *this;
		}
	private:
		T* _ptr;
	};
}
void TestAuto_ptr()
{
	vs::auto_ptr<int> ps1(new int);
	vs::auto_ptr<int> ps2(ps1);  //因为ps2是最后创建的,auto_ptr就会猜测之前的ps1你可能不用了,然后在智能指针内部,让指针指向空
	//*ps1 = 10;   指针指向空之后,这个语句报错,此时ps1已经是个空对象
	vs::auto_ptr<int> ps3;
	ps3 = ps2;
}
int main()
{
	TestAuto_ptr();
	return 0;
}

Here Insert Picture Description
Why use a smart pointer to manage the application space, space applications directly to wait until the end of the program, or the user manual release, the resources will be cleaned up from memory, but users will forget to release, plus in because of abnormal, often let the program launched reason is right, sometimes the release of resources is not fully taken into account the location, so write the code is likely to cause resource leaks, so only the smart pointer.
Here Insert Picture Description

So how to solve the problem when the transfer of resources is brought before the object is no longer to power this space can be used, if the object is not to point to empty before, then again there are a number of pointers to release a space, when released will cause the collapse of the code for this release.

So on the introduction of a flag
to add a member variable bool _owner;
but after the transfer of resources to an object occur then this object will no longer have the power to release this space, this time _owner becomes false, when an object . it has the right to release the shared resources when this time _owner is true

then the code auto_ptr would write:

对上面的auto_ptr的改进:
//析构函数
~auto_ptr()
{
	if(_ptr && _owner)   //资源存在,切拥有释放权
	{
		delete _ptr;
		_ptr = nullptr;
	}
}
// 拷贝构造函数
auto_ptr<T>& operator=(auto_ptr<T>& ps)
	:_ptr(ap._ptr)
	,_owner(true)
{
	ap._owner = false;
}
//赋值运算符重载:
auto_ptr<T>& operator=(auto_ptr<T>& ps)
{
	if (this != &ps)
	{
		if (!this && _owner == true)   //如果当前对象已经管理资源了,就要先把当前对象的资源先释放
			delete _ptr;
		_ptr = ps._ptr;
		_owner = true;
		ps._owner = false;
	}
	return *this;
}

The principle of auto_ptr improved:
RAII + operator * () + operator -> () + _owner

But _owner version of auto_ptr defects: may cause dangling pointer - but also may cause the collapse of the code

Why is prone to wild pointer it? Because if there are multiple objects point to a resource, although it has the power to release only one object of this resource, if the resource is released, this time to continue to reference other objects of this resource without knowing, you can not using this memory resources, then those objects inside the pointer becomes a wild pointer.

The second version solves the problem can not reference multiple objects of the first version, but it introduces a new and more serious problem is the field guide, it is the transfer of resources to use auto_ptr standard
it gives advice yes, do not use auto_ptr under what circumstances

Usually by the general pointers to understand the smart pointer:
Here Insert Picture Description
Whether smart pointer is implemented in what way:

Principles of smart pointers: RAII (automatic release resources) + operator * + operator-> (keeping the original ecological functions) to solve the shallow copy +

Here to talk about solving shallow copy, is to prevent the collapse of the time out of scope, smart pointers times caused by the release of resources.

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//C++11:auto_ptr---->保留:资源转移实现
//就是为了防止之前其他人写的代码中使用了auto_ptr编译不能通过
//C++11提供了更靠谱的智能指针
//unique_ptr 唯一的指针,
//浅拷贝发生的原因就是拷贝构造函数和赋值运算符引起的,所以unique_ptr就是不提供这两个函数,就没法进行浅拷贝
template<class T>
class DFDef
{
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;
		}
	}
};
class FClose
{
public:
	void operator()(FILE*& p)
	{
		if (p)
		{
			fclose(p);
		}
	}
};
namespace vs
{
	template<class T, class DF = DFDef<T>>
	class unique_ptr
	{
	public:
		unique_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{}
		~unique_ptr()
		{
			if (_ptr)
			{
				//delete _ptr;  //释放资源的方式固定死了,不能处理任意类型的资源,只能处理new类型的空间
				//DF()(_ptr);
				//上面这句代码 分成两句也就是
				DF df;
				df(_ptr);
				_ptr = nullptr;
			}
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		//解决浅拷贝:禁止调用拷贝构造函数,禁止调用拷贝构造函数
		unique_ptr(const unique_ptr<T>&) = delete;
		unique_ptr<T>& operator=(const unique_ptr<T>&) = delete;
	private:
		T* _ptr;
	};
}
void TestUniquePtr()
{
	vs::unique_ptr<int> up1(new int);
	vs::unique_ptr<int, Free<int>> up2((int*)malloc(sizeof(int)));
	vs::unique_ptr<FILE, FClose> up3(fopen("1.txt","w"));

}
int main()
{
	TestUniquePtr();
	return 0;
}
Published 230 original articles · won praise 28 · views 9337

Guess you like

Origin blog.csdn.net/weixin_43767691/article/details/103159812