为什么c ++用零来初始化std :: vector,而不是std :: array?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么c ++用零来初始化std :: vector,而不是std :: array?相关的知识,希望对你有一定的参考价值。
当你不想要它时,用零来初始化矢量不是浪费时间吗?
我试试这段代码:
#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("
");
return 0;
}
我得到了输出:
与矢量
$ g++ -std=c++11 -D VECTOR test.cpp -o test && ./test
0 0 0 0 0 0 0 0 0 0
用数组
g++ -std=c++11 test.cpp -o test && ./test
-129655920 32766 4196167 0 2 0 4196349 0 1136 0
我也尝试使用clang ++
为什么零呢?顺便说一句,我可以在不初始化的情况下声明一个向量吗?
假设我们有一些课程:
class MyClass {
int value;
public:
MyClass() {
value = 42;
}
// other code
};
std::vector<MyClass> arr(10);
将默认构建10份MyClass
,全部使用value = 42
。
但是假设它没有默认构建10个副本。现在如果我写了arr[0].some_function()
,那就有一个问题:MyClass
的构造函数还没有运行,因此没有设置类的不变量。我可能在some_function()
的实现中假设value == 42
,但由于构造函数没有运行,value
有一些不确定的值。这将是一个错误。
这就是为什么在C ++中,有一个object lifetimes的概念。在调用构造函数之前,该对象不存在,并且在调用析构函数后它不再存在。 std::vector<MyClass> arr(10);
调用每个元素的默认构造函数,以便存在所有对象。
重要的是要注意std::array
有点特别,因为它是initialized following the rules of aggregate initialization。这意味着std::array<MyClass, 10> arr;
也默认使用MyClass
构建10个value = 42
副本。但是,对于非类型类型(如unsigned
),值将是不确定的。
有一种方法可以避免调用所有默认构造函数:std::vector::reserve
。如果我写:
std::vector<MyClass> arr;
arr.reserve(10);
向量将分配其后备数组以保存10个MyClass
s,并且它不会调用默认构造函数。但现在我不能写arr[0]
或arr[5]
;这些将是越界访问arr
(arr.size()
仍然是0,即使支持数组有更多的元素)。要初始化值,我必须调用push_back
或emplace_back
:
arr.push_back(MyClass{});
这通常是正确的方法。例如,如果我想用arr
中的随机值填充std::rand
,我可以使用std::generate_n
和std::back_inserter
:
std::vector<unsigned> arr;
arr.reserve(10);
std::generate_n(std::back_inserter(arr), 10, std::rand);
值得注意的是,如果我已经在容器中拥有我想要的arr
值,我可以使用构造函数传递begin()/end()
:
std::vector<unsigned> arr{values.begin(), values.end()};
声明向量的更常见方法是不指定大小:
std::vector<unsigned> arr;
这不会为向量内容分配任何空间,也没有任何初始化开销。元素通常使用.push_back()
等方法动态添加。如果你想分配内存,你可以使用reserve()
:
arr.reserve(SIZE);
这不会初始化添加的元素,它们不包含在向量的size()
中,并且尝试读取它们是未定义的行为。比较这个
arr.resize(SIZE);
它会增长向量并初始化所有新元素。
另一方面,std::array
总是分配内存。它实现了与C风格数组相同的大多数行为,除了指针的自动衰减。这包括默认情况下不初始化元素。
默认分配器正在进行零初始化。您可以使用不这样做的其他分配器。我写了一个分配器,它在可行时使用默认构造而不是初始化。更确切地说,它是一个名为ctor_allocator
的分配器包装器。然后我定义了一个vector
模板。
dj:vector<unsigned> vec(10);
完全符合你的要求。这是一个未初始化为零的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)...);
}
};
}
以上是关于为什么c ++用零来初始化std :: vector,而不是std :: array?的主要内容,如果未能解决你的问题,请参考以下文章