[英]Why does c++ initialise a std::vector with zeros, but not a std::array?
Isn't it a waste of time to initialize a vector with zeros, when you don't want it? 当你不想要它时,用零来初始化矢量不是浪费时间吗?
I try this code: 我试试这段代码:
#include <iostream>
#include <vector>
#include <array>
#define SIZE 10
int main()
{
#ifdef VECTOR
std::vector<unsigned> arr(SIZE);
#else
std::array<unsigned, SIZE> arr;
#endif // VECTOR
for (unsigned n : arr)
printf("%i ", n);
printf("\n");
return 0;
}
and I get the output: 我得到了输出:
with vector 与矢量
$ g++ -std=c++11 -D VECTOR test.cpp -o test && ./test
0 0 0 0 0 0 0 0 0 0
with an array 用数组
g++ -std=c++11 test.cpp -o test && ./test
-129655920 32766 4196167 0 2 0 4196349 0 1136 0
And I also try with clang++ 我也尝试使用clang ++
So why zeros? 为什么零呢? And by the way, could I declare a vector without initializing it?
顺便说一句,我可以在不初始化的情况下声明一个向量吗?
The more common way to declare a vector is without specifying the size: 声明向量的更常见方法是不指定大小:
std::vector<unsigned> arr;
This doesn't allocate any space for the vector contents, and doesn't have any initialization overhead. 这不会为向量内容分配任何空间,也没有任何初始化开销。 Elements are usually added dynamically with methods like
.push_back()
. 元素通常使用
.push_back()
等方法动态添加。 If you want to allocate memory you can use reserve()
: 如果要分配内存,可以使用
reserve()
:
arr.reserve(SIZE);
This doesn't initialize the added elements, they're not included in the size()
of the vector, and trying to read them is undefined behavior. 这不会初始化添加的元素,它们不包含在向量的
size()
中,并且尝试读取它们是未定义的行为。 Compare this with 比较这个
arr.resize(SIZE);
which grows the vector and initializes all the new elements. 它会增长向量并初始化所有新元素。
std::array
, on the other hand, always allocates the memory. 另一方面,
std::array
总是分配内存。 It implements most of the same behaviors as C-style arrays, except for the automatic decay to a pointer. 它实现了与C风格数组相同的大多数行为,除了指针的自动衰减。 This includes not initializing the elements by default.
这包括默认情况下不初始化元素。
The default allocator is doing the zero-initialization. 默认分配器正在进行零初始化。 You can use a different allocator that does not do that.
您可以使用不这样做的其他分配器。 I wrote an allocator that uses default construction rather than initialization when feasible.
我写了一个分配器,它在可行时使用默认构造而不是初始化。 More precisely, it is an allocator-wrapper called
ctor_allocator
. 更确切地说,它是一个名为
ctor_allocator
的分配器包装器。 Then I define a vector
template. 然后我定义了一个
vector
模板。
dj:vector<unsigned> vec(10);
does exactly what you want. 完全符合你的要求。 It's an
std::vector<unsigned> (10)
that is not initialized to zeros. 这是一个未初始化为零的
std::vector<unsigned> (10)
。
--- libdj/vector.h ----
#include <libdj/allocator.h>
#include <vector>
namespace dj {
template<class T>
using vector = std::vector<T, dj::ctor_allocator<T>>;
}
--- libdj/allocator.h ----
#include <memory>
namespace dj {
template <typename T, typename A = std::allocator<T>>
class ctor_allocator : public A
{
using a_t = std::allocator_traits<A>;
public:
using A::A; // Inherit constructors from A
template <typename U> struct rebind
{
using other =
ctor_allocator
< U, typename a_t::template rebind_alloc<U> >;
};
template <typename U>
void construct(U* ptr)
noexcept(std::is_nothrow_default_constructible<U>::value)
{
::new(static_cast<void*>(ptr)) U;
}
template <typename U, typename...Args>
void construct(U* ptr, Args&&... args)
{
a_t::construct(static_cast<A&>(*this),
ptr, std::forward<Args>(args)...);
}
};
}
Suppose we have some class: 假设我们有一些课程:
class MyClass {
int value;
public:
MyClass() {
value = 42;
}
// other code
};
std::vector<MyClass> arr(10);
will default construct 10 copies of MyClass
, all with value = 42
. 将默认构造10个
MyClass
副本,全部value = 42
。
But suppose it didn't default construct the 10 copies. 但是假设它没有默认构建10个副本。 Now if I wrote
arr[0].some_function()
, there's a problem: MyClass
's constructor has not yet run, so the invariants of the class aren't set up. 现在如果我写了
arr[0].some_function()
,就会出现问题: MyClass
的构造函数还没有运行,所以没有设置类的不变量。 I might have assumed in the implementation of some_function()
that value == 42
, but since the constructor hasn't run, value
has some indeterminate value. 我可能在
some_function()
的实现中假设value == 42
,但由于构造函数没有运行,因此value
具有一些不确定的值。 This would be a bug. 这将是一个错误。
That's why in C++, there's a concept of object lifetimes . 这就是为什么在C ++中,有一个对象生命周期的概念。 The object doesn't exist before the constructor is called, and it ceases to exist after the destructor is called.
在调用构造函数之前,该对象不存在,并且在调用析构函数后它不再存在。
std::vector<MyClass> arr(10);
calls the default constructor on every element so that all the objects exist. 调用每个元素的默认构造函数,以便存在所有对象。
It's important to note that std::array
is somewhat special, since it is initialized following the rules of aggregate initialization . 重要的是要注意
std::array
有点特殊,因为它是按照聚合初始化规则初始化的 。 This means that std::array<MyClass, 10> arr;
这意味着
std::array<MyClass, 10> arr;
also default constructs 10 copies of MyClass
all with value = 42
. 默认情况下,还会构造10个
MyClass
副本,其value = 42
。 However, for non-class types such as unsigned
, the values will be indeterminate. 但是,对于非类型类型(如
unsigned
,值将是不确定的。
There is a way to avoid calling all the default constructors: std::vector::reserve
. 有一种方法可以避免调用所有默认构造函数:
std::vector::reserve
。 If I were to write: 如果我写:
std::vector<MyClass> arr;
arr.reserve(10);
The vector would allocate its backing array to hold 10 MyClass
s, and it won't call the default constructors. 向量将分配其后备数组以保存10个
MyClass
,并且它不会调用默认构造函数。 But now I can't write arr[0]
or arr[5]
; 但现在我不能写
arr[0]
或arr[5]
; those would be out-of-bounds access into arr
( arr.size()
is still 0, even though the backing array has more elements). 那些将是越界的访问
arr
( arr.size()
仍然是0,即使支持数组有更多的元素)。 To initialize the values, I'd have to call push_back
or emplace_back
: 要初始化值,我必须调用
push_back
或emplace_back
:
arr.push_back(MyClass{});
This is often the right approach. 这通常是正确的方法。 For example, if I wanted to fill
arr
with random values from std::rand
, I can use std::generate_n
along with std::back_inserter
: 例如,如果我想用
std::rand
随机值填充arr
,我可以使用std::generate_n
和std::back_inserter
:
std::vector<unsigned> arr;
arr.reserve(10);
std::generate_n(std::back_inserter(arr), 10, std::rand);
It's also worth noting that if I already have the values I want for arr
in a container, I can just pass the begin()/end()
in with the constructor: 值得注意的是,如果我已经在容器中拥有了我想要的
arr
值,我可以使用构造函数传递begin()/end()
:
std::vector<unsigned> arr{values.begin(), values.end()};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.