我现在正在学习C ++。这是一种如此复杂的语言,我不确定应该使用哪种功能以及何时使用。
C ++ Primer引入RAII作为确保异常安全的方法。这是否意味着,作为一种好的行为,当我想使用数组时,我应该将数组放入一个类来分配和销毁资源。我知道我的想法很简单,或者天真。
我只是好奇什么是好的C ++编码行为。
答案 0 :(得分:4)
RAII表示资源与对象生命周期相关联
每个C ++类的C ++都遵循这个原则,这意味着如果你需要一个数组,你只需使用std::vector
。
当实例超出范围时,vector类的析构函数将负责删除资源。
这意味着在您的情况下,而不是像这样使用新的:
int *array = new int[n];
你应该使用:
vector<int> array(n);
如果你真的需要在堆上分配一个共享指针并且仍然可以通过使用RAII来保证安全(需要C ++ 11):
shared_ptr<vector<int>> array(new vector<int>(10));
答案 1 :(得分:2)
这取决于您创建阵列的方式。如果你在像这样的代码块中创建它
int arr[5];
它具有自动存储功能。当该数组超出范围时,该数组将自动销毁。
您必须明确管理对象生命周期的唯一时间是动态分配它们,可能是这样的:
int* arr = new int[5];
这会分配一个5 int
的数组,需要手动delete
d后面这样:
delete[] arr;
RAII是我们可以让类的构造函数执行动态分配并且它的析构函数执行释放的想法。然后,如果我们创建此类类型的对象,它们将在内部执行动态分配,但我们可以确保在完成时将正确释放内存。
所以是的,如果你真的想要一个动态分配的数组,你可以将它封装在你自己的类中。但是,这已经以更强大的形式存在:std::vector
。 std::vector
类型在内部管理动态分配的数组,但也允许您在运行时向其中添加元素和从中删除元素。
请注意,这不是数组特有的。最佳实践是在类中封装所有动态分配(甚至是简单的new int
)。标准库提供了许多类来帮助您(特别是智能指针)。
答案 2 :(得分:1)
筹码: 请考虑以下代码:
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运算符。每当使用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 - 除非你创建一个指针容器,或者在堆上分配你的vector。
我希望有所帮助。