Effective Modern C++读书笔记:条款18

Aki 发布于 2023-01-14 231 次阅读


条款18使用std:;unique_ptr管理具备专属所有权的资源:

每当你需要使用智能指针的时候,std::unique_ptr基本是最合适的。可以认为在默认情况下,std::unique_ptr性能等同于原始指针,而且对于大多数操作(包括解引用),它们执行的指令完全相同。这意味着你甚至可以在内存和时间都比较紧张的情况下使用它。如果原始指针够小够快,那么std::unique_ptr几乎可以肯定也能满足你的要求。

std::unique_ptr体现了专有所有权语义。一个非空的std::unique_ptr总是拥有其所指向的资源。移动一个std::unique_ptr会将所有权从源指针移动到目标指针(原指针被置空)。拷贝操作是不允许的,因为如果你能拷贝一个std::unique_ptr,你会得到指向相同内容的两个std::unique_ptr,每个都认为自己拥有资源,销毁时也会出现重复销毁。因此,std::unique_ptr只支持移动操作。std::unique_ptr销毁时,其指向的资源也执行析构函数。而原始指针需要显式调用delete来销毁指针指向的资源

std::unique_ptr有两种形式,一种用于单个对象(std::unique_ptr<T>),一种用于数组(std::unique_ptr<T[]>数组的std::unique_ptr的存在应该不被使用,因为std::arraystd::vectorstd::string这些更好用的数据容器应该取代原始数组

unique_ptr特点:

在没有自定义析构器的情况下,可以认为unique_ptr和裸指针大小相同

但如果有自定义析构器,就不同了。

  • 如果自定义析构器是函数指针,那么std::unique_ptr的尺寸一般会增加1到2个字长。
  • 如果自定义析构器是函数对象,那么std::unique_ptr的尺寸取决于该函数对象中存储了多少状态。特别指出,无状态的函数对象(例如,无捕获的lambda表达式)不会浪费存储尺寸

意味着,如果自定义表达器,既可以用函数也可以用无捕获的lambda表达式的时候,lambda表达式会是更好的选择。


class Test
{
public:
	Test()noexcept
	{

	}
	~Test()noexcept
	{
		
	}
	

};

auto deleter = [](Test* p)
{
	if (p)
	{
		delete p;
	}
	cout << "自定义删除器调用" << endl;
};


int main()
{

	
	unique_ptr<Test, decltype(deleter)> p{ new Test{},deleter };
	cout << sizeof(p) << endl;

	return 0;
}

std::unique_ptr也可以很方便的转换成std::shared_ptr

unique_ptr<Test> p{ new Test{}};
shared_ptr<Test> q = make_shared<Test>(p);

要点速记
1. std::unique_ptr是小巧的,高速的,只能移动的智能指针,对托管资源实施专属所有权语义。
2. 默认的,析构器采用delete实现,也可以指定自定义析构器。但有状态捕获和函数指针的实现方式会增加std::unique_ptr变量的尺寸。
3. 将std::unique_ptr转换成std::shared_ptr是很方便的。