2016-10-23 8 views
6

Z kompilatorem g ++ 5.4 i biblioteką standardową GNU libstdC++. So.6, domyślny konstruktor z std::vector tworzy pusty kontener inicjalizujący tylko wewnętrzne elementy danych księgowych na stosie. Przydziela bufor na stertę dla elementów danych później (po wstawieniu pierwszego elementu). Do niedawna uważałem, że jest to typowe zachowanie dla każdego standardowego kontenera sekwencyjnego z dynamicznie przydzielaną pamięcią.Dlaczego std :: deque przydziela pamięć dla swoich elementów w domyślnym konstruktorze?

Jednak std::deque działa inaczej. Kalka następujący kod

#include <deque> 
int main() { 
    std::deque<int> d; 
    return 0; 
} 

z ltrace daje

__libc_start_main(0x4024fa, 1, 0x7ffd088be0f8, 0x405bd0 <unfinished ...> 
_ZNSt8ios_base4InitC1Ev(0x608351, 0xffff, 0x7ffd088be108, 160)  = 0 
__cxa_atexit(0x401f20, 0x608351, 0x608210, 0x7ffd088bded0)   = 0 
_Znwm(64, 8, 0, 8)             = 0x7b4c20 
_Znwm(512, 128, 0, 128)            = 0x7b4c70 
_ZdlPv(0x7b4c70, 0x7b4c70, 128, 0x7b4c70)        = 1 
_ZdlPv(0x7b4c20, 0x7b4c20, 8, 0x7b4c20)        = 0 
_ZNSt8ios_base4InitD1Ev(0x608351, 0, 0x401f20, 0x7fc6d4567d10)  = 0x7fc6d4b00880 
+++ exited (status 0) +++ 

_Znwm jest implementacją operator new (patrz this). Uwzględniając internal structure of std::deque a linia od nagłówka realizacji STL bits/stl_deque.h#define _GLIBCXX_DEQUE_BUF_SIZE 512, można stwierdzić, że _Znwm(64, 8, 0, 8) przydziela mapę std::deque „s do 8 wskaźników i _Znwm(512, 128, 0, 128) przydziela jeden klocek 512 bajtów.

Pytanie brzmi: jaki jest powód, aby nie odkładać przydzielania tych 512 bajtów do momentu wstawienia pierwszego elementu w std::deque?

+3

Czy jesteś pewien, że twoje instrukcje są poprawne zgodnie ze standardem C++? – juanchopanza

+0

Opisałem szczegółowo moją implementację. – ktrushin

+0

Może możesz dodać te informacje do swojego pytania. – juanchopanza

Odpowiedz

0

Faktycznie według normy (summarized by cppreference) Domyślne konstruktor

explicit deque(const Allocator& alloc = Allocator()); 

konstruktor domyślny. Konstruuje pusty kontener.

Jeśli twój konkretny kompilator zdecydował, że implementacja domyślnego konstruktora przydzieli trochę pamięci, to był to wybór właściwy dla danej implementacji.

+0

Dlaczego wdrożenie może dokonać tego wyboru? Czy ten wybór ma dobre uzasadnienie z twojego punktu widzenia? – ktrushin

+0

To samo odniesienie później określa "stałą złożoności (1)". Jeśli cppreference ma być zaufany, nie sądzę, że jest to implementacja zgodna. – StoryTeller

+6

@StoryTeller złożoność tej implementacji wydaje się być stała. Dlaczego uważasz, że to nie jest zgodne? – user2079303

1

Najbardziej prawdopodobną przyczyną jest to, że jeśli implementacja się nie powiodła, każde polecenie push_back(), push_front(), begin(), end(), insert() musiałoby sprawdzić, czy blok wskaźników do stron jest przydzielana, a jeśli nie, wykonaj przydział bloku wskaźników + pierwsza strona. To warunkowe sprawdzenie spowalnia te operacje, a liczba tych operacji prawdopodobnie zmniejszy liczbę wykonywanych konstruktów deque.

Wdrożenie podjęło decyzję o zablokowaniu minimalnego bloku wskaźników + ewentualnie pustej strony. Skutecznie jest jak wartownik.

Czy naprawdę wolisz, aby te wspólne funkcje były wolniejsze?

+2

W każdym razie 'push_back()' i inny członek funkcje, o których wspomniałeś, muszą sprawdzić, czy odpowiednia porcja (strona w twojej terminologii) ma wolną przestrzeń do umieszczenia dodawanego elementu i przydzielić inną porcję, jeśli to konieczne. Z mojego punktu widzenia samo sprawdzanie nie spowalnia znacząco funkcji członków. Jest przydzielana pamięć, która to robi. Zapewniając, że nie możemy uniknąć alokacji pamięci we wspomnianych funkcjach członkowskich, dodanie kolejnej nie zmieni znacząco sytuacji. Proszę popraw mnie jeżeli się mylę. – ktrushin

Powiązane problemy