我是否应该将 RAII 应用于我分配的所有阵列

Should I apply RAII to all arrays I allocated?

本文关键字:阵列 分配 RAII 是否 应用于      更新时间:2023-10-16

我现在正在学习C++。这是一种非常复杂的语言,我不确定我应该使用哪个功能以及何时使用。

C++ Primer引入了RAII作为确保异常安全的方法。这是否意味着,作为一个好的行为,当我想使用数组时,我应该将数组放入一个类中来分配和销毁资源。我知道我的想法很简单,或者很幼稚。

我只是好奇什么是好的C++编码行为。

RAII 表示资源与对象生存期相关联。
每个 STL 类C++都遵循这一原则,这意味着如果您需要一个数组,您可以简单地使用 std::vector .

向量类的析构函数将负责在实例超出范围时删除资源。

这意味着在您的情况下,而不是像这样使用 new:

int *array = new int[n];

您应该使用:

vector<int> array(n);

如果您确实需要在堆上分配以拥有共享指针,并且仍然使用 RAII 是安全的,您可以这样做(需要 C++11):

shared_ptr<vector<int>> array(new vector<int>(10));

这取决于您如何创建数组。如果你在这样的代码块中创建它

int arr[5];

它具有自动存储功能。当此数组超出范围时,它会自动销毁。

唯一需要显式管理对象生存期的情况是动态分配对象,可能如下所示:

int* arr = new int[5];

这将分配一个 5 个 int s 的数组,稍后需要手动delete d,如下所示:

delete[] arr;

RAII 的想法是,我们可以让类的构造函数执行动态分配,其析构函数执行释放。然后,如果我们创建此类类型的对象,它们将在内部执行动态分配,但我们可以确保在完成后将正确释放内存。

所以是的,如果你真的想要一个动态分配的数组,你可以把它封装在你自己的类中。但是,这已经以更强大的形式存在:std::vector .std::vector 类型在内部管理动态分配的数组,但也允许您在运行时向其添加和删除元素。

请注意,这不是特定于数组的内容。最佳实践是将所有动态分配(即使是简单的new int)封装在类中。标准库提供了许多类来帮助您执行此操作(特别是智能指针)。

RAII 是资源管理的推荐用语 - 资源可以是线程、内存...... 我经常遇到不了解 RAII 或如何使用它的长期开发人员,所以问这个问题并不丢人。 不幸的是,我认为您需要一些背景和术语才能理解它。 在C++中,您可以在以下两种内存之一中创建对象:堆栈和堆。

堆栈:请考虑以下代码:

class Foo
{
    // some implementation
}
void bar()
{
  Foo f;
  // do something with f.
  // maybe it throws an exception, maybe not.
}

这里,Foo类型的对象f已在堆栈上创建。 当正在运行的程序离开创建 f 的作用域时(在我们的示例中,当它离开函数 bar() 时),f 的析构函数将被调用,无论它如何离开作用域。 它可能会因为函数成功执行而离开作用域,或者可能会因为引发异常而离开作用域。

堆分配现在考虑以下几点:

class Foo
{
// same thing
}
void bar()
{
Foo* f = new f;
// whatever
delete f;
}

在本例中,我们在上创建了对象 f。 你可以说我们这样做是因为我们打电话给新的接线员。 每当使用 new 创建对象时,我们都必须调用 delete 以释放内存。 这可能容易出错,因为人们忘记调用 delete,或者他们返回指针并且不清楚指针应该在哪里删除,或者因为抛出了异常。 RAII是所有这些的解决方案,它应该是您的首选工具。

这并不意味着你应该把你的数组放在一个类中(在某些情况下确实如此,但可能不是你的意思)。 请考虑以下事项:

void foo()
{
     // c-style array, but fixed size.
     int bar_stack[5];
     // c-style array, dynamically allocated!
     unsigned int array_size = get_array_size();
     int* bar_heap = new int[array_size]
     // c++ way:
     std::vector bar_vector(array_size);
 }

在第一个例子中,我们有一个固定的 c 样式数组,它是在堆栈上分配的,所以没有什么可担心的。 在第二种情况下,它是动态分配的(编译器不知道大小,它在运行时可用),因此我们需要删除,这意味着我们需要防止异常并确保有一个明确的所有者。 在第三种情况下,我们只使用一个向量,这是一个标准容器

现在我提供了所有这些,因为我担心你不会理解简短的答案,即:是的,你通常应该使用 RAII 来管理你的资源,但你应该使用 std 容器,而不是数组。 为什么? 因为 std 容器为您提供了 RAII - 除非您制作一个指针容器,或者在堆上分配向量。

我希望这有所帮助。