類應該是被封裝的,類的用戶通過介面使用類提供的功能,而不必關心類的內部如何實現。然而,C++標準庫容器 std::vector 的實現滲透到了介面中來。對於以下代碼: const int pushNum = 10; std::vector<int> v = { 1,2,3 }; int* p = & ...
類應該是被封裝的,類的用戶通過介面使用類提供的功能,而不必關心類的內部如何實現。然而,C++標準庫容器 std::vector 的實現滲透到了介面中來。對於以下代碼:
const int pushNum = 10; std::vector<int> v = { 1,2,3 }; int* p = &v[1]; std::cout << "*p = " << * p << std::endl; std::cout << "v[1] = " << v[1] << std::endl;
for (int i = 0; i < pushNum; i++) v.push_back(i); std::cout << "---------------------" << std::endl;
std::cout << "*p = " << *p << std::endl; std::cout << "v[1] = " << v[1] << std::endl;
我們初始化了一個有3個int元素的vector,定義了一個int 指針p,指向v[1] , 列印 *p 以及v[1] 的值。 然後向 v 中push_back() 了10 個元素,之後再次列印 *p 以及 v[1] 的值。在作者的環境中,程式的運行結果如下:
可以看到,在最初的v 中,列印出的*p 以及 v[1] 的值都是2, 這正是容器中下標為 1 的元素的值。 而push_back() 一些元素之後,v[1] 仍然是2,但是*p 的值卻變了。指針p指向的就是v[1] ,為什麼會這樣呢?原因要追溯到標準庫std::vector的實現。
std::vector 對象動態管理記憶體空間,一個std::vector 對象所分配的所有記憶體空間未必都構造了元素。可以使用其成員函數size() 返回其已構造的元素數量, 使用capacity() 返回其已分配的總容量,即,已分配的記憶體一共可以構造多少個元素。隨著我們不斷push_back(),size 會逐漸增加,直到capacity沒有足夠的空間能夠容下下一個元素,這時,std::vector就會分配更多的記憶體。然而,std::vector 要求元素是連續存儲的,如果此時連續的記憶體已經沒有更多的空間了,std::vector會把所有元素搬到一塊其他的,更大的記憶體空間,來容納更多元素,並且其內部實現會保證其重載的下標訪問運算符 [ ] 是有效的,然而std::vector 無法得知此時有一個指針p指向了容器內的元素,所以無法更新指針,而指針所指向的舊的記憶體現在是未知的。
那麼std::vector 每次重新分配的時候會申請多大的空間作為capacity() ? 作者在自己的環境中做了一些實驗,但並沒有發現什麼規律。