【问题标题】:How to initialize std::vector from C-style array?如何从 C 样式数组初始化 std::vector?
【发布时间】:2011-01-26 21:53:27
【问题描述】:

从 C 样式数组初始化 std::vector 最便宜的方法是什么?

示例:在下面的类中,我有一个vector,但是由于外界的限制,数据会以C风格的数组的形式传入:

class Foo {
  std::vector<double> w_;
public:
  void set_data(double* w, int len){
   // how to cheaply initialize the std::vector?
}

显然,我可以调用w_.resize(),然后循环遍历元素,或者调用std::copy()。有没有更好的方法?

【问题讨论】:

  • 问题的症结在于,向量无法知道是否使用相同的分配器来创建您的 C 样式数组。因此,向量必须使用自己的分配器分配内存。否则它可以简单地换出底层数组并将其替换为您的数组。

标签: c++ arrays vector stl


【解决方案1】:

不要忘记您可以将指针视为迭代器:

w_.assign(w, w + len);

【讨论】:

  • 这是一个实施质量问题。由于迭代器具有指定其类别的标签,assign 的实现当然可以免费使用它们进行优化;至少在 VC++ 中,它确实做到了。
  • 快速解决方案可能是 std::vector w_(w,w+len);
  • 这会将元素复制到新创建的“w_”存储中; 'w_.data' 不会指向 'w'。你仍然必须解除分配'w'。没有所有权转让
  • 如果它是 one 元素在末尾之后,它应该没问题(就像v.end() 是一个迭代器,在类似的情况下用向量指向末尾)。如果你确实得到了一个断言,那么其他地方就有问题了。
  • 快点,当向量超出范围时,这会释放数组内存吗?
【解决方案2】:

您使用了初始化这个词,因此不清楚这是一次性分配还是可以多次发生。

如果你只需要一次初始化,你可以把它放在构造函数中,使用两个迭代器向量构造函数:

Foo::Foo(double* w, int len) : w_(w, w + len) { }

否则按照前面的建议使用assign:

void set_data(double* w, int len)
{
    w_.assign(w, w + len);
}

【讨论】:

  • 在我的情况下,分配会重复发生。
【解决方案3】:

好吧,Pavel 很接近,但还有一个更简单优雅的解决方案可以从 c 样式数组初始化顺序容器。

在你的情况下:

w_ (array, std::end(array))
  • array 会给我们一个指向数组开头的指针(没有找到它的名字),
  • std::end(array) 将为我们提供一个指向数组末尾的迭代器。

【讨论】:

  • 这需要哪些 C++/版本?
  • 这是至少从 c++98 开始的 std::vector 的构造函数之一......它被称为“范围构造函数”。 cplusplus.com/reference/vector/vector/vector 试试看。
  • 更独立的版本是:w_ (std::begin(array), std::end(array)); (将来您可以为 C++ 容器更改 C 数组)。
  • 请注意,这仅在您拥有真正的 array 时才有效(这通常意味着您正在从全局或本地(在当前函数中声明)数组进行复制)。在 OP 的情况下,他正在接收一个指针和一个长度,并且因为它没有在长度上进行模板化,所以它们不能更改为接收指向一个大小数组或任何东西的指针,所以 std::end 将不起作用。
  • vector 不会重载operator(),因此无法编译。在指针上调用std::end 也是没有用的(问题要求从指针和单独的长度变量分配一个向量)。这将改善您的答案,以显示有关您要建议的内容的更多上下文
【解决方案4】:

您可以自动“了解”数组的大小:

template<typename T, size_t N>
void set_data(const T (&w)[N]){
    w_.assign(w, w+N);
}

希望您可以将接口更改为上面的 set_data。它仍然接受一个 C 风格的数组作为它的第一个参数。它只是碰巧引用它。


工作原理

[ 更新:请参阅here 了解有关学习大小的更全面的讨论]

这是一个更通用的解决方案:

template<typename T, size_t N>
void copy_from_array(vector<T> &target_vector, const T (&source_array)[N]) {
    target_vector.assign(source_array, source_array+N);
}

这是有效的,因为数组是作为对数组的引用传递的。在 C/C++ 中,您不能将数组作为函数传递,而是会衰减为指针并且您会丢失大小。但在 C++ 中,您可以传递对数组的引用。

通过引用传递数组需要类型完全匹配。数组的大小是其类型的一部分。这意味着我们可以使用模板参数 N 来为我们学习大小。

让这个函数返回一个向量可能更简单。通过适当的编译器优化,这应该比看起来要快。

template<typename T, size_t N>
vector<T> convert_array_to_vector(const T (&source_array)[N]) {
    return vector<T>(source_array, source_array+N);
}

【讨论】:

  • 在最后一个示例中,return { begin(source_array), end(source_array) }; 也是可以的
【解决方案5】:

快速通用答案:

std::vector<double> vec(carray,carray+carray_size); 

或具体问题:

std::vector<double> w_(w,w+len); 

基于above不要忘记您可以将指针视为迭代器

【讨论】:

    【解决方案6】:

    std::vector&lt;double&gt;::assign 是要走的路,因为它是小代码。但实际上它是如何工作的?它不会调整大小然后复制吗?在 STL 的 MS 实现中,我正在使用它。

    恐怕没有更快的方法来实现(重新)初始化您的std::vector

    【讨论】:

    • 如果数据在向量和数组之间共享怎么办?在这种情况下我们需要复制任何东西吗?
    • 这是一个答案还是一个问题?它为已有的答案带来了什么?
    • @Jean-FrançoisFabre 你的评论带来了什么? ;) 是的,这是很久以前给出的糟糕答案。
    猜你喜欢
    • 2010-09-18
    • 2013-07-25
    • 2020-01-19
    • 2016-05-08
    • 1970-01-01
    • 2013-06-06
    • 2018-10-17
    • 1970-01-01
    • 2016-07-30
    相关资源
    最近更新 更多