簡體   English   中英

為什么push_back或push_front使deque的迭代器無效?

[英]Why does push_back or push_front invalidate a deque's iterators?

正如標題所要求的那樣。

我對雙端隊列的理解是它分配了“塊”。 我沒有看到如何分配更多的空間使迭代器無效,如果有的話,人們會認為deque的迭代器比矢量更有保證,而不是更少。

C ++標准沒有規定deque的實現方式。 不需要通過分配新的塊並將其鏈接到先前的塊來分配新的空間,所需要的只是在每一端的插入是分攤的常量時間。

因此,雖然很容易看到如何實現deque以便它提供您想要的保證[*],但這不是唯一的方法。

[*]迭代器具有對元素的引用,以及對它所在塊的引用,以便它們在到達時可以繼續向前/向后退出塊的末尾。 另外,我假設對deque本身的引用,因此operator+可以按照隨機訪問迭代器的預期進行恆定時間 - 從塊到塊之間的鏈接鏈不夠好。

更有趣的是push_backpush_front 不會使對deque元素的任何引用無效。 只有迭代器才被認為是無效的。

據我所知,標准沒有說明原因。 但是,如果實現了一個知道其直接鄰居的迭代器 - 如列表所示 - 如果迭代器指向一個同時位於雙端隊列邊緣和塊邊緣的元素,那么迭代器將變為無效。

我猜。 push_back / push_front可以分配新的內存塊。 deque迭代器必須知道遞增/遞減運算符何時應跳轉到下一個塊。 實現可以將該信息存儲在迭代器本身中。 push_back / push_front之后遞增/遞減舊迭代器可能無法按預期工作。

此代碼可能會或可能不會因運行時錯誤而失敗。 在我的Visual Studio上,它在調試模式下失敗,但在發布模式下運行結束。 在Linux上它導致了分段錯誤。

#include <iostream>
#include <deque>

int main() {
    std::deque<int> x(1), y(1);
    std::deque<int>::iterator iterx = x.begin();
    std::deque<int>::iterator itery = y.begin();

    for (int i=1; i<1000000; ++i) {
        x.push_back(i);
        y.push_back(i);
        ++iterx;
        ++itery;
        if(*iterx != *itery) {
            std::cout << "increment failed at " << i << '\n';
            break;
        }
    }
}

關鍵是不做任何假設只是將迭代器視為無效。

即使它現在工作正常,編譯器的更高版本或不同平台的編譯器可能會出現並破壞您的代碼。 或者,同事可能會出現並決定將您的雙端隊列轉換為向量或鏈接列表。

迭代器不僅僅是對數據的引用。 它必須知道如何增量等。

為了支持隨機訪問,實現將具有指向塊的動態指針數組。 deque迭代器將指向此動態數組。 當deque增長時,可能需要分配新的塊。 動態數組將增長,使其迭代器無效,從而使deque的迭代器無效。

所以不是重新分配塊,而是指向這些塊的指針數組。 實際上,正如約翰內斯紹布所指出的,參考文獻並未失效。

另請注意,deque的迭代器保證不小於向量,當容器增長時也會失效。

即使在分配塊時,如果沒有足夠的空間,插入也會導致重新分配該特定塊(如向量的情況)。

因為標准說它可以。 它並沒有強制要求將deque實現為塊列表。 它要求具有特定前后條件和特定算法復雜度最小值的特定接口。

只要滿足所有這些要求,實現者就可以以他們選擇的任何方式自由地實現該事物。 一個明智的實現可能會使用塊列表,或者它可能會使用其他一些具有不同權衡的技術。

對於所有情況下的所有用戶來說,可能無法說一種技術嚴格優於另一種技術。 這就是為什么該標准賦予實現者一定的選擇自由。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM