在构造函数或初始化函数中分配内存?

Alloc memory in constructor or init function?

我是 C++ 新手,我有一个 class 保存一些内存,class 看起来像:

class MyClass
{
public:
    MyClass (int s)
    {
        if (s <= 0) _size = 1;
        else _size = s;

        data = new int[_size];  // may throw exception
    }


private:
    int *data;
    int _size;
};

据我所知,在构造函数中抛出异常是不安全的,所以我将 malloc 放到了一个初始化函数中。


class MyClass
{
public:
    MyClass (int s)
    {
        if (s <= 0) _size = 1;
        else _size = s;
    }

    void init()
    {
        data = new int[_size];
    }


private:
    int *data;
    int _size;
};

我的问题:

  1. 在构造函数或初始化函数中分配内存,哪个更好
  2. 如果我选择了init函数,在调用其他成员函数之前如何确保init函数已经被调用?

构造函数的关键在于您可以确保构造函数在任何其他成员函数之前被调用。

初始化函数来自 c,不被认为是最佳实践

To my knowledge, throw exception in constructor is unsafe, so I put the malloc to a init function.

不,这不是“不安全”。这是使构造函数失败的方法。您可能正在考虑析构函数中止或其他异常安全保证问题。

“Init”函数为构造函数引入了一种两阶段方法,增加了复杂性。除非您需要在没有例外的环境中工作,否则请避免使用它们。在这种情况下,工厂函数是另一种做事的方式。

memory alloc in constructor or init function, which is better

构造函数。参见 std::vector

if I chose init function, how to ensure init function has called before call other member function?

如果没有 运行 时间检查或外部工具,静态地你不能。

您可以在 init 函数上设置一个标志,然后签入每个成员函数。

if I chose init function, how to ensure init function has called before call other member function?

不行,这是个大问题!您在这里偶然发现了一个名为 Resource Acquisition is Initialisation (RAII) 的概念。这就是你在第 1 部分中所写的大致内容。如果你添加一个析构函数:

~MyClass() {
    delete[] data;
}

那么您已经开始致力于最有用的 c++ 容器之一,std::vector。这与您在此处尝试实现的目标类似。

关于这个甚至有一个 cpp 核心指南:A constructor should create a fully initialised object

To my knowledge, throw exception in constructor is unsafe

实际上这是不正确的。事实上,下一个核心准则是 If a constructor cannot construct a valid object, throw an exception.

TLDR:使用选项一,在这种情况下初始化是错误的。

除了上述有效答案外,您还可以考虑使用现有标准库“封装”内存的分配和取消分配 class:std::unique_ptr<int>. Using it may save you the need to implement a bunch of methods (e.g. copy constructor, move constructor, assignment operator and destructor; see the rule of five).

此外,std::size_t 更适合存储已分配内存的数量。

最后,_开头的名字一般都是保留的,我们尽量避免。

所以:

#include <memory>

// ...

class MyClass {
public:
    using size_type = std::size_t;
    using value_type = int;

    MyClass (size_type size) :
        size_(std::max<size_type>(size,1)),
        data_(std::make_unique<value_type[]>(size_))
    { }

    // etc. etc. - but no need for copy ctor, move ctor, assignments and dtor

private:
    std::unique_ptr<value_type[]> data_;
    size_type size_;
};