《Effictive C++》学习笔记 — 资源管理
条款13 — 以对象管理资源
1、使用goto的资源管理
在C和C++中对goto关键字的使用被诟病违背了结构化编程。然而,由于内存资源是由我们自己维护的,很多情况下我们又不得不使用goto保证内存的释放。
#include <iostream>
using namespace std;
class CLS_Test
{
public:
bool open()
{
return true;
}
bool start()
{
throw exception("start exception");
}
bool stop()
{
return false;
}
~CLS_Test()
{
cout << "release CLS_Test" << endl;
}
};
void test()
{
CLS_Test* pTest = new CLS_Test;
if (!pTest)
{
goto END;
}
if (!pTest->open())
{
goto END;
}
try
{
if (!pTest->start())
{
goto END;
}
}
catch (exception e)
{
cout << e.what() << endl;
goto END;
}
if (!pTest->stop())
{
goto END;
}
END:
if (pTest)
{
delete pTest;
}
}
int main()
{
test();
}
一方面,为了使代码清晰易读,我们需要使用goto语句;同时,我们也是为了避免资源泄漏。这么一看其实goto可以跟java中的finally类比。
即使如此,以后可能有其他人对此段代码进行修改。若他们没有注意到此处的资源管理,而是在中间return了,就有可能导致资源泄漏。就像我们前面讨论过的,良好的编程风格固然可以避免一些问题。然而如果我们能从语法角度直接杜绝问题产生的可能性,总是更好的。
2、以对象管理资源
这种思想基于以下的事实:在函数运行结束后,临时类对象的析构函数一定会被自动调用。
因此,我们可以使用C++标准库中提供的智能指针来管理堆中的内存。
void test()
{
std::auto_ptr<CLS_Test> pTest(new CLS_Test);
if (!pTest->open())
{
return;
}
try
{
if (!pTest->start())
{
return;
}
}
catch (exception e)
{
cout << e.what() << endl;
}
if (!pTest->stop())
{
return;
}
}
(1)获得资源后立刻放进管理对象内
在这里我们明确提出管理对象的概念。智能指针本身是一个对象,但是其作用只是用于管理我们在堆上分配的内存。通常情况下,我们会将内存块作为智能指针的初值。因此通常我们认为:资源取得时机便是初始化时机(RAII)。 这样的做法也是最安全有效的。
(2)运用析构函数确保资源释放
在智能指针内部,会在析构函数里对其管理的内存进行释放。我们也正是使用了析构函数的必然执行来保障了内存的管理。那么对于析构函数中可能抛出的异常呢?我们前面在前面讨论过,客户有机会处理异常。
3、C++标准库中的智能指针
简单讨论下C++中的四种智能指针。
(1)auto_ptr
最早的一个智能指针。它在C++11中被标注为过时,C++17中已移除。前面我们的例子就是使用它实现的,因此我们简单说下它的一个特性:
同时保证只有一个智能指针对象拥有指针的控制权。
auto_ptr& operator=(auto_ptr& _Right) noexcept {
reset(