[英]Why does c++ initialise a std::vector with zeros, but not a 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("\n");
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 ++
為什么零呢? 順便說一句,我可以在不初始化的情況下聲明一個向量嗎?
聲明向量的更常見方法是不指定大小:
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)...);
}
};
}
假設我們有一些課程:
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 ++中,有一個對象生命周期的概念。 在調用構造函數之前,該對象不存在,並且在調用析構函數后它不再存在。 std::vector<MyClass> arr(10);
調用每個元素的默認構造函數,以便存在所有對象。
重要的是要注意std::array
有點特殊,因為它是按照聚合初始化規則初始化的 。 這意味着std::array<MyClass, 10> arr;
默認情況下,還會構造10個MyClass
副本,其value = 42
。 但是,對於非類型類型(如unsigned
,值將是不確定的。
有一種方法可以避免調用所有默認構造函數: std::vector::reserve
。 如果我寫:
std::vector<MyClass> arr;
arr.reserve(10);
向量將分配其后備數組以保存10個MyClass
,並且它不會調用默認構造函數。 但現在我不能寫arr[0]
或arr[5]
; 那些將是越界的訪問arr
( arr.size()
仍然是0,即使支持數組有更多的元素)。 要初始化值,我必須調用push_back
或emplace_back
:
arr.push_back(MyClass{});
這通常是正確的方法。 例如,如果我想用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);
值得注意的是,如果我已經在容器中擁有了我想要的arr
值,我可以使用構造函數傳遞begin()/end()
:
std::vector<unsigned> arr{values.begin(), values.end()};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.