对象生存期和资源管理 (RAII)

与托管语言不同,C++ 没有自动回收垃圾,这是在程序运行时释放堆内存和其他资源的一个内部进程。 C++ 程序负责将所有已获取的资源返回到操作系统。 未能释放未使用的资源称为“泄漏”。 在进程退出之前,泄漏的资源无法用于其他程序。 特别是内存泄漏是 C 样式编程中 bug 的常见原因。

新式 C++ 通过声明堆栈上的对象,尽可能避免使用堆内存。 当某个资源对于堆栈来说太大时,则它应由对象拥有。 当该对象初始化时,它会获取它拥有的资源。 然后,该对象负责在其析构函数中释放资源。 在堆栈上声明拥有资源的对象本身。 对象拥有资源的原则也称为“资源获取即初始化”(RAII)。

当拥有资源的堆栈对象超出范围时,会自动调用其析构函数。 这样,C++ 中的垃圾回收与对象生存期密切相关,是确定性的。 资源始终在程序中的已知点发布,你可以控制该点。 仅类似 C++ 中的确定析构函数可公平处理内存和非内存资源。

下面的示例显示了简单查询 w。 它在函数范围内的堆栈上声明,并在函数块的末尾销毁。 对象 w 没有资源(例如堆分配的内存)。 它的唯一成员 g 本身在堆栈上声明,并且与 w 一起超出范围。 widget 析构函数中不需要特殊代码。

class widget {
private:
    gadget g;   // lifetime automatically tied to enclosing object
public:
    void draw();
};

void functionUsingWidget () {
    widget w;   // lifetime automatically tied to enclosing scope
                // constructs w, including the w.g gadget member
    // ...
    w.draw();
    // ...
} // automatic destruction and deallocation for w and w.g
  // automatic exception safety,
  // as if "finally { w.dispose(); w.g.dispose(); }"

在以下示例中,w 拥有内存资源,因此必须在其析构函数中具有代码才能删除内存。

class widget
{
private:
    int* data;
public:
    widget(const int size) { data = new int[size]; } // acquire
    ~widget() { delete[] data; } // release
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data member
    w.do_something();

} // automatic destruction and deallocation for w and w.data

自 C++11 以来,有一种更好的方法可以编写前面的示例:使用标准库中的智能指针。 智能指针可处理对其拥有的内存的分配和删除。 使用智能指针将不需要在 widget 类中显式析构函数。

#include <memory>
class widget
{
private:
    std::unique_ptr<int[]> data;
public:
    widget(const int size) { data = std::make_unique<int[]>(size); }
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data gadget member
    // ...
    w.do_something();
    // ...
} // automatic destruction and deallocation for w and w.data

使用智能指针进行内存分配,可以消除内存泄漏的可能性。 此模型适用于其他资源,例如文件句柄或套接字。 可以在你的类中以类似的方式管理自己的资源。 有关详细信息,请参阅智能指针

C++ 的设计可确保对象在超出范围时被销毁。 也就是说,它们在块被退出时以与构造相反的顺序被摧毁。 销毁对象时,将按特定顺序销毁其基项和成员。 在全局范围内在任何块之外声明的对象可能会导致问题。 如果全局对象的构造函数引发异常,将很难调试。

另请参阅

欢迎回到 C++
C++ 语言参考
C++ 标准库