在构造函数初始化列表中分配内存是否存在任何问题

Are there any issues with allocating memory within constructor initialization lists?

本文关键字:是否 存在 任何 问题 内存 分配 构造函数 初始化 列表      更新时间:2023-10-16

我在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时,使用它是否安全,或者是否存在我可能会违反的某些特殊初始化顺序?

它不是异常安全的。如果jnew抛出异常,则不会调用Test的析构函数,因此不会释放i的内存。

如果j的初始值设定项抛出,就会调用i的析构函数,这只是原始指针没有析构函数。因此,您可以通过用合适的智能指针替换i来确保异常安全。在这种情况下,iunique_ptr<int>junique_ptr<int[]>就可以了

您可以依赖初始值设定项以其正确的顺序执行(成员的定义顺序,而不一定是列表中的顺序)。它们可以安全地使用已经初始化的数据成员,因此在k的初始化器中使用count没有问题。

当初始化程序抛出异常时,此代码可能会泄漏内存。

请注意,如果Test的成员是智能指针而不是原始指针,则可以使其正常工作。

从初始值设定项列表中调用new没有特定问题。

然而,如果您为多个成员执行此操作,则不会出现异常安全,并且有泄漏内存的风险(如果第一个new调用成功,但第二个调用抛出异常怎么办?那么第一个分配就会泄漏)。

至于依赖初始化顺序,这是完全安全的。成员按照它们在类声明中列出的顺序进行初始化。因此,您可以使用早期初始化的成员的值来初始化"稍后"的成员。

请记住,决定初始化顺序的是它们在类中的声明顺序,而不是它们在初始化列表中的顺序。:)

您正在初始化程序列表中分配内存;这是完全可以的,但是您要将指向该内存的指针分配给原始指针。

这些原始指针并不意味着它们所指向的指针有任何内存所有权或删除,因此,代码包含多个内存泄漏,不遵循"五条规则",而且通常很糟糕。

编写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])
    {
    }
};

初始化没有问题——标准保证它们按顺序进行,但请记住,如果这些分配中的任何一个失败,前一个分配都不会被释放。

因此,唯一的缺点是,如果您想防止分配失败,那么您更希望将它们初始化为nil,并在构造函数中将分配封装在try块中。不过,通常不需要这样做,除非您的应用程序面临内存不足的真正危险,并且需要从中恢复。

当然,假设只有内存不足才能引发异常,这也是正确的——如果你分配了可以引发其他异常的对象,你应该更担心它

假设您有:

class Foo
{
public:
    T* p1;
    T* p2;
    Foo()
    : p1(new T),
      p2(new T)
    {
    }
};

如果初始化p2失败(因为new抛出内存不足异常,或者因为T构造函数失败),则p1将被泄漏。为了解决这个问题,C++允许在初始化列表中使用try/catch,但这通常很粗糙

Steve Jessop的回答存在陷阱。

关于订单,我认为这就是你的问题所解决的:

12.6.2/4

初始化应按以下顺序进行:

[…]

  • 然后,非静态数据成员应按照它们在类定义中声明的顺序进行初始化(同样,无论顺序如何mem初始化器的)

因为在您的类中,count是在k之前声明的,所以您可以。