【问题标题】:How to initialize elements of an array managed by a unique_ptr?如何初始化由 unique_ptr 管理的数组元素?
【发布时间】:2019-02-12 11:17:46
【问题描述】:

我们知道资源获取是初始化 (RAII),我寻找了初始化具有参数的对象数组的语法(没有默认参数),由 unique_ptr 管理,但我没有找到任何示例,有Cppreference 中的一个构造 int

int size = 10; 
std::unique_ptr<int[]> fact(new int[size]);

我怎么会这样写:

class Widget
{
 Widget(int x, in y):x_(x),y_(y)
 {}
 int x_,y_;
};

 std::unique_ptr<Widget[]> fact(new Widget[size]);

【问题讨论】:

标签: c++ c++11 std unique-ptr


【解决方案1】:

按照推荐链接中的最后一个答案
How can I make new[] default-initialize the array of primitive types?
我想出了以下小例子:

#include <iostream>
#include <memory>
#include <string>

class Widget {
  private:
    std::string _name;
  public:
    Widget(const char *name): _name(name) { }
    Widget(const Widget&) = delete;
    const std::string& name() const { return _name; }
};

int main()
{
  const int n = 3;
  std::unique_ptr<Widget[]> ptrLabels(
    new Widget[n]{
      Widget("label 1"),
      Widget("label 2"),
      Widget("label 3")
    });
  for (int i = 0; i < n; ++i) {
    std::cout << ptrLabels[i].name() << '\n';
  }
  return 0;
}

输出:

label 1
label 2
label 3

Live Demo on coliru

诀窍是使用初始化列表。

我有点不确定这是否涉及复制构造(这在小部件类库中经常被禁止)。可以肯定的是,我写了Widget(const Widget&amp;) = delete;

我不得不承认这适用于 C++17,但不适用于之前。


我对第一个例子做了一点改动。

我也试过了

new Widget[n]{
  { "label 1" },
  { "label 2" },
  { "label 3" }
});

直到我意识到我忘记在第一个示例中创建构造函数explicit 之前都成功了。 (通常,小部件集不允许这样做 - 以防止意外转换。)修复此问题后,它不再编译。

介绍,即使使用 C++11 也可以编译的移动构造函数:

#include <iostream>
#include <memory>
#include <string>

class Widget {
  private:
    std::string _name;
  public:
    explicit Widget(const char *name): _name(name) { }
    Widget(const Widget&) = delete;
    Widget(const Widget &&widget): _name(std::move(widget._name)) { }
    const std::string& name() const { return _name; }
};

int main()
{
  const int n = 3;
  std::unique_ptr<Widget[]> ptrLabels(
    new Widget[n]{
      Widget("label 1"),
      Widget("label 2"),
      Widget("label 3")
    });
  for (int i = 0; i < n; ++i) {
    std::cout << ptrLabels[i].name() << '\n';
  }
  return 0;
}

输出:如上

Live Demo on coliru

【讨论】:

    【解决方案2】:

    您可以使用放置新和自定义删除器:

    class Widget {
    public:
        int i;
        Widget(int i) : i(i) {}
        ~Widget() { std::cout << i; }
    };
    
    class WidgetDeleter {
        int _size;
    public:
        WidgetDeleter(int size) : _size(size) {}
        void operator()(Widget* w) { 
            for (int i = 0; i < _size; ++i) w[i].~Widget();
        }
    }; 
    
    void main() {
        const int widgetsCount = 10;
        auto widgets = std::unique_ptr<Widget[], WidgetDeleter>(
            (Widget*)(new byte[widgetsCount * sizeof(Widget)]), WidgetDeleter(widgetsCount));
        for (int i = 0; i < widgetsCount; ++i) new (widgets.get() + i)Widget(i);
        for (int i = 0; i < widgetsCount; ++i) std::cout << widgets[i].i;
        std::cout << std::endl;
    }
    

    正如预期的那样,我们有两行输出:

    0123456789
    0123456789

    注意,因为这里的删除器是有状态的,所以不可能使用std::unique_ptr&lt;Widget[], WidgetDeleter&gt;[unique.ptr.single.ctor#8]的默认构造函数

    【讨论】:

    • 如果Widget的构造函数可能抛出异常怎么办?为了异常安全,您的代码将变得非常复杂。而且,您最终只需手动实现std::vector 为您提供的内容。
    • @DanielLangr 我同意这样的代码应该仔细编写并保存在库中。
    猜你喜欢
    • 2017-11-06
    • 2020-04-17
    • 1970-01-01
    • 1970-01-01
    • 2019-11-07
    • 1970-01-01
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    相关资源
    最近更新 更多