7

我需要一个既不能复制也不能移动的元素容器。这些元素不是默认可构造的,但它们的构造函数得到相同的参数。

容器的大小在其生命周期内不会改变。它应该像内置数组一样简单,但它的大小是在运行时调用构造函数时确定的。

有没有一种简单的方法来实现它而不会产生内存分配和间接使用的开销std::vector<std::unique_ptr<T>>

4

4 回答 4

3

这是一个简单但不完整的解决方案,假设每个元素都是用相同的参数构造的。它使用放置new来就地构造元素(另请参阅此 SO question):

#include <cstdlib>
#include <utility>
#include <new>

// sample structure, non-copyable, non-moveable, non-default-constructible
struct Foo
{
  Foo() = delete;
  Foo(const Foo&) = delete;
  Foo& operator = (const Foo&) = delete;
  Foo(Foo&&) = delete;
  Foo& operator = (Foo&&) = delete;

  Foo(int a, char b, double c) : m_a(a), m_b(b), m_c(c) { }

  int m_a;
  char m_b;
  double m_c;
};

template <typename T>
struct MyArray
{
  // Array ctor constructs all elements in-place using the
  // provided parameters
  template <typename... Args>
  MyArray(std::size_t sz, Args&&... args)
    : m_sz(sz),
      m_data(static_cast<T*>(malloc(sz * sizeof(T))))
  {
    for (std::size_t i=0; i<m_sz; ++i)
    {
      new (&m_data[i]) T(std::forward<Args>(args)...);
    }
  }

  ~MyArray()
  {
    for (std::size_t i=0; i<m_sz; ++i)
    {
      m_data[i].~T();
    }
    free(m_data);
  }

  std::size_t m_sz;
  T *m_data;
};

int main()
{
  Foo foo(1, '2', 3.0);
  std::size_t s = 5;
  MyArray<Foo> foo_arr(s, 1, '2', 3.0);
}

请注意,缺少一些内容:

  • MyArray如果在的构造函数中抛出异常,这个基本实现将泄漏内存。
  • 您可能需要一个迭代器实现、begin()/end()运算符等,以获得更多便利并获得与标准容器提供的相同行为。
  • 为了说明起见,我也没有为适当的封装而烦恼。您可能应该制作m_szm_data私人成员。
于 2016-09-29T13:17:46.930 回答
0

我试图做同样的事情,我正在使用一个简单的解决方法。这个例子有什么问题?

class test{
  const int a;
public:
  test(int i): a(i) {} //no default constructor
};

//trick for static initializer
template <typename T> class defer_params: public T{
public:
  defer_params(): T(1) {} //fixed params
};

//static array
//test list1[5]; //this doesn't work
defer_params<test> list2[5];

问候,加布里埃尔

于 2022-02-21T15:53:06.803 回答
0

由于您要管理的元素既不可移动也不可复制,因此容器只能包含指向元素的指针。在不了解更多或您的要求的情况下,很难猜测原始指针是否std::unique_ptr更合适。

对于固定大小的容器, astd::array可能是合适的。不幸的是,大小必须是编译时表达式。

另一种方法是使用原始数组,并使用placement new 来构建元素。您可以尝试使用向量而不是原始数组,因为向量使用像普通数组一样的连续内存,但我无法想象如何使用它来存储不可复制、不可移动和不可默认构造的对象。

顺便说一句,在 C++ 中编译时大小未知的非默认可构造对象上构建数组并不是那么简单。我能找到的唯一方法是构建一个适当大小的 char 数组,声明一个指向指向数组开头的类的指针,然后使用放置 new 构建元素:

char buf = new char[n * sizeof(X)];
X* x = reinterpret_cast<X*>(buf);
for(int i=-1; i<n i++) {
    new (x + i) X(i);  // or whatever appropriate ctor...
}
于 2016-09-29T13:20:33.380 回答
-1

“有没有一种简单的方法来实现它,而无需使用 std::vector> 产生的内存分配和间接开销?”

开销如此之小,你为什么在乎?这几乎可以肯定是过早的优化,你只会让自己在维护方面头疼。

于 2016-09-29T15:23:47.060 回答