我在C ++程序中使用了很多初始化列表但是没有意识到你可以在其中分配内存。
所以你可以这样做(作为一个人为的例子):
class Test
{
private:
int* i;
int* j;
int count;
int* k;
public:
Test(void) : i(new int), j(new int[10]), count(10), k(new int[count])
{
}
~Test(void)
{
delete i;
delete [] j;
delete [] k;
}
};
以这种方式进行内存分配是否有任何问题?关于初始化的顺序,在同一列表中初始化的参数初始化参数是否安全?即我在使用它之前分配count
是否可以安全使用,或者是否有一些特殊的初始化顺序我可能会犯错?
答案 0 :(得分:20)
这不是例外安全的。如果new
j
抛出异常,则不会调用Test
的析构函数,因此i
的内存不会被释放。
如果i
的初始化程序抛出,则调用j
的析构函数,它只是原始指针没有析构函数。因此,您可以通过使用合适的智能指针替换i
来使其异常安全。在这种情况下,unique_ptr<int>
为i
,unique_ptr<int[]>
为j
。
您可以依赖初始化程序以正确的顺序执行(定义成员的顺序,不一定是列表中的顺序)。他们可以安全地使用已经初始化的数据成员,因此在count
的初始值设定项中使用k
没有问题。
答案 1 :(得分:4)
如果存在抛出异常的初始化程序,此代码可能会泄漏内存。
请注意,如果Test
的成员是聪明的而不是原始指针,则可以使其正常工作。
答案 2 :(得分:3)
您正在初始化列表中分配内存;这完全没问题,但是你将指向该内存的指针分配给原始指针。
这些原始指针并不意味着任何类型的内存所有权或删除它们指向的指针,因此,代码包含几个内存泄漏,不遵循“五条规则”,并且通常是错误的
编写Test
的更好方法是:
class Test
{
private:
//Assuming you actually want dynamic memory allocation:
std::unique_ptr<int> i;
std::unique_ptr<int[]> j;
int count;
std::unique_ptr<int[]> k;
public:
Test(void) : i(new int), j(new int[10]), count(10), k(new int[count])
{
}
};
答案 3 :(得分:2)
初始化没有问题 - 标准要保证它们按顺序完成,但是,请记住,如果这些分配失败,前者将不会被释放。
所以唯一的缺点是,如果你想保持安全以防止失败的分配 - 那么你宁愿将它们初始化为nil
,并在构造函数中将分配包装在try
中块。但通常不需要这样做,除非您的应用程序存在内存不足的真正危险,并且需要从中恢复。
当然,假设只有缺少内存可以抛出异常,那就成了现实 - 如果你分配的对象可以抛出其他异常,你应该更担心它。
答案 4 :(得分:1)
从初始化列表中调用new
没有具体问题。
但是,如果你为多个成员执行它,它将不会是异常安全的,并且你冒着泄漏内存的风险(如果第一个new
调用成功,但是第二个抛出异常会怎么样?那么第一个分配被泄露了。)
至于依赖初始化顺序,这是非常安全的。成员按照它们在类声明中列出的顺序进行初始化。因此,您可以使用早期初始化的成员的值来初始化“以后”成员。
请记住,这是它们在类中的声明顺序,而不它们在初始化列表中的顺序决定了它们的初始化顺序。 :)
答案 5 :(得分:1)
假设你有:
class Foo
{
public:
T* p1;
T* p2;
Foo()
: p1(new T),
p2(new T)
{
}
};
如果初始化p2
失败(因为new
抛出内存不足异常或因T
构造函数失败),则p1
将被泄露。为了解决这个问题,C ++允许在初始化列表中使用try
/ catch
,但它通常非常粗略。
答案 6 :(得分:1)
关于订单,我认为是您的问题所针对的:
12.6.2 / 4
初始化应按以下顺序进行:
[...]
- 然后,非静态数据成员应按照在类定义中声明的顺序进行初始化(再次无论顺序如何 mem-initializers)。
因为在课堂上count
在k
之前宣布,你就可以了。