![](/img/trans.png)
[英]Does std::vector<Simd_wrapper> have contiguous data in memory?
[英]How does std::vector support contiguous memory for custom objects of unknown size
我正在為正確的思維模式和對std::vector
理解而苦苦掙扎。
當創建類型T的向量,然后為該向量保留N個元素時,編譯器基本上會找到並保留一個連續的內存塊,即N * sizeof(T)
個字節。 例如,
// Initialize a vector of int
std::vector<int> intvec;
// Reserve contigious block of 4 4-byte chunks of memory
intvec.reserve(4); // [ | | | ]
// Filling in the memory chunks has obvious behavior:
intvec.push_back(1); // [1| | | ]
intvec.push_back(2); // [1|2| | ]
然后,我們可以在隨機訪問時間內訪問任何元素,因為如果我們要求向量的第k個元素,我們只需從向量開始處的內存地址開始,然后“跳轉” k * sizeof(T)
字節即可獲得到第k個元素。
我的思維模型針對大小未知/不同的自定義對象進行了分解。 例如,
class Foo {
public:
Foo() = default;
Foo(std::vector<int> vec): _vec{vec} {}
private:
std::vector<int> _vec;
};
int main() {
// Initialize a vector Foo
std::vector<Foo> foovec;
// Reserve contigious block of 4 ?-byte chunks of memory
foovec.reserve(4); // [ | | | ]
// How does memory allocation work since object sizes are unkown?
foovec.emplace_back(std::vector<int> {1,2}); // [{1,2}| | | ]
foovec.emplace_back(std::vector<int> {1,2,3,4,5}); // [{1,2}|{1,2,3,4,5}| | ]
return 0;
}
由於我們不知道Foo每個實例的大小,因此foovec.reserve()
如何分配內存? 此外,您如何獲得隨機訪問時間,而我們不知道要“跳”多遠才能到達第k個元素?
您的尺寸概念有缺陷。 一個std::vector<type>
具有一個已知的編譯時要占用的空間大小。 它還具有可以使用的運行時大小(在運行時分配,並且向量保存指向它的指針)。 您可以像這樣布置圖片
+--------+
| |
| Vector |
| |
| |
+--------+
|
|
v
+-------------------------------------------------+
| | | | | |
| Element | Element | Element | Element | Element |
| | | | | |
+-------------------------------------------------+
因此,當您擁有包含向量的事物的向量時,每個Element
將成為向量,然后指向其他地方的自身存儲
+--------+
| |
| Vector |
| |
| |
+----+---+
|
|
v
+----+----+---------+---------+
| Object | Object | Object |
| with | with | with |
| Vector | Vector | Vector |
+----+----+----+----+----+----+
| | | +---------+---------+---------+---------+---------+
| | | | | | | | |
| | +-->+ Element | Element | Element | Element | Element |
| | | | | | | |
| | +-------------------------------------------------+
| | +-------------------------------------------------+
| | | | | | | |
| +--->+ Element | Element | Element | Element | Element |
| | | | | | |
| +-------------------------------------------------+
| +-------------------------------------------------+
| | | | | | |
+--->+ Element | Element | Element | Element | Element |
| | | | | |
+---------+---------+---------+---------+---------+
這樣,所有向量都彼此相鄰,但是向量具有的元素可以在內存中的其他任何位置。 因此,您不想將std:vector<std::vector<int>>
用於矩陣。 所有子向量都將存儲到任何地方,因此行之間沒有局部性。
請注意,這適用於所有可識別分配器的容器,因為它們不會將元素直接存儲在容器中。 對於std::array
,情況並非如此,因為像原始數組一樣,元素是容器的一部分。 如果您有std::array<int, 20>
,則至少為sizeof(int) * 20
個字節。
的大小
class Foo {
public:
Foo() = default;
Foo(std::vector<int> vec): _vec{vec} {}
private:
std::vector<int> _vec;
};
內部常量std :: vector是已知且恆定的,它會在堆中進行分配,因此foovec.reserve(4);
沒問題foovec.reserve(4);
否則std :: vector怎么會在堆棧中? ;-)
Foo
類的大小在編譯時是已知的, std::vector
類具有恆定的大小,因為它持有的元素是在堆上分配的。
std::vector<int> empty{};
std::vector<int> full{};
full.resize(1000000);
assert(sizeof(empty) == sizeof(full));
盡管持有不同數量的元素, std::vector<int>
兩個實例, empty
和full
都將始終具有相同的大小。
如果您想要一個無法調整大小的數組,並且必須在編譯時知道其大小,請使用std::array
。
創建T類型的向量並為該向量保留N個元素時,編譯器基本上會找到並保留一個連續的內存塊
編譯器不執行此類操作。 它在運行時生成代碼以請求向量的分配器進行存儲。 默認情況下,它是std::allocator
,它委派給operator new
,該operator new
將從運行時系統中獲取未初始化的存儲。
我的心理模型針對大小未知/變化的自定義對象分解
用戶定義類型實際上可以具有未知大小的唯一方法是不完整-並且您不能將向量聲明為不完整類型。
在你的代碼中的任何點的種類齊全 ,其大小也是固定的,你可以聲明存儲該類型像往常一樣的向量。
您的Foo
已完成,並且其大小在編譯時已固定。 您可以使用sizeof(Foo)
和sizeof(foovec[0])
等進行檢查。
向量擁有可變數量的存儲空間,但不包含在對象中。 它只是存儲一個指針以及保留和使用的大小(或等效值)。 例如,以下實例:
class toyvec {
int *begin_;
int *end_;
size_t capacity_;
public:
// push_back, begin, end, and all other methods
};
始終具有固定的大小sizeof(toyvec) = 2 * sizeof(int*) + sizeof(size_t) + maybe_some_padding
。 分配一個巨大的內存塊,並將設置begin
,對指針本身的大小沒有影響。
tl; dr C ++沒有動態調整大小的對象。 對象的大小由類定義永久固定。 C ++ 確實擁有一些對象,這些對象擁有並且可能會調整動態存儲的大小,但這不是對象本身的一部分。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.