2013-02-25 10 views
14

Mam konkretne pytanie jest to, że przy wdrażaniu singleton class W C++ jest tam żadnych zasadniczych różnic między tymi dwoma poniżej kody dotyczące wydajności, kwestii ubocznych lub coś:Heap/dynamiczne vs. statyczne przydzielanie pamięci na przykład C++ klasy Singleton

class singleton 
{ 
    // ... 
    static singleton& getInstance() 
    { 
     // allocating on heap 
     static singleton* pInstance = new singleton(); 
     return *pInstance; 
    } 
    // ... 
}; 

a to:

class singleton 
{ 
    // ... 
    static singleton& getInstance() 
    { 
     // using static variable 
     static singleton instance; 
     return instance; 
    } 
    // ... 
}; 


(Zauważ, że w realizacji dereferencing sterty oparte nie powinny wpływać na wydajność, jak AFAIK nie ma dodatkowych maszynowego kod generowany do dereferencji. To wydaje się tylko kwestią składni odróżnić od wskaźników)

UPDATE:.

Mam ciekawe odpowiedzi i komentarze, które staram się podsumować je tutaj. (Odczytywanie szczegółowe odpowiedzi jest zalecany dla osób zainteresowanych.):

  • W Singleton przy użyciu statycznego zmiennej lokalnej, destruktor klasy jest automatycznie wywoływana na zakończenie procesu, podczas gdy w dynamicznej alokacji przypadku masz w jakiś sposób zarządzać niszczeniem obiektów, np za pomocą inteligentnych wskazówek:
static singleton& getInstance() { 
     static std::auto_ptr<singleton> instance (new singleton()); 
     return *instance.get(); 
    } 
  • Singletor stosując dynamiczną alokację jest „leniwy” niż zmiennej statycznej Singleton, jak w ostatnim przypadku wymagana pamięć dla singleton obiektu jest (zawsze ?) zarezerwowane przy uruchomieniu procesu (jako część całej pamięci wymaganej do załadowania programu) i tylko wywołanie konstruktora singleton jest odroczone do czasu wywołania getInstance(). Może to mieć znaczenie, gdy sizeof(singleton) jest duże.

  • Obie są wątkowo bezpieczne w C++ 11. Ale we wcześniejszych wersjach C++ jest to specyficzne dla implementacji.

  • Przypadku przydzielania dynamicznego używa jednego poziomu dwukierunkowości, aby uzyskać dostęp do obiektu singleton, podczas gdy w przypadku pojedynczego obiektu statycznego singleton, adres bezpośredni obiektu jest określany i zakodowany na stałe podczas kompilacji.


P.S .: Mam poprawione terminologii ja używany w oryginalnym delegowania według odpowiedzią @ Tonyd użytkownika.

+0

Czy porównałeś wygenerowany zestaw dla dwóch? –

+0

Nie. Jeśli masz na myśli wygenerowany zestaw dla dwóch różnych implementacji, są one oczywiście różne, ponieważ jeden przydziela stertę, a drugi inicjuje ładowanie/wywoływanie. Jeśli masz na myśli wygenerowany zestaw do dereferencji, nie, nie porównałem. Po prostu tak. –

Odpowiedz

7
  • wersja new oczywiście musi przydzielić pamięci w czasie wykonywania, podczas gdy wersja non-wskaźnik ma pamięć przydzieloną w czasie kompilacji (ale oba trzeba zrobić taką samą konstrukcję)

  • new wersja nie wywoła destruktor obiektu na zakończenie programu, ale nie- new wersja: można użyć inteligentnego wskaźnika do skorygowania tego

    • trzeba uważać, że niektóre statyczne/namespace-scope obiektu destruktory nie wywołują twojego singletona po uruchomieniu jego destruktora statycznej lokalnej instancji ... jeśli jesteś zaniepokojony tym, powinieneś być może przeczytać nieco więcej o wcieleniach Singleton i podejściach do zarządzania nimi. Nowoczesne C++ Design Andrei Alexandrescu ma bardzo czytelny zabieg.
  • pod C++ 03, jest to definicja implementacji, czy będzie bezpieczny wątek. (Wydaje mi się, że GCC wydaje się być, podczas gdy Visual Studio dąży do tego, by nie potwierdzić/nie docenić).

  • pod C++ 11, jest bezpieczny: 6.7.4 "Jeśli sterowanie wprowadza deklarację jednocześnie, gdy zmienna jest przy inicjalizacji, współbieżne wykonanie powinno czekać na zakończenie inicjalizacji. " (rekursja sans).

ponownej dyskusji w czasie kompilacji kontra run-time przydziału & inicjalizacji

Z okazji masz sformułowanego podsumowanie i kilka komentarzy, podejrzewam, nie jesteś całkowicie zrozumienia subtelny aspekt alokacji i inicjalizacji zmiennych statycznych ....

że Twój program ma 3 lokalnych statycznych 32-bitowy int S - a, b i c - w różnych funkcjach: kompilacji r prawdopodobnie skompiluje plik binarny, który mówi programowi ładującemu systemowi operacyjnemu, aby pozostawił 3x32-bitów = 12 bajtów pamięci dla tych statystyk. Kompilator decyduje, na czym polega przesunięcie każdej z tych zmiennych: może umieścić a przy przesunięciu 1000 hex w segmencie danych, b przy 1004 i c przy 1008. Po uruchomieniu programu, program ładujący OS nie musi przydzielać pamięci dla każdy osobno - wszystko, co wie, to w sumie 12 bajtów, które może, ale nie musi, zostać specjalnie zaproszone do zainicjowania 0, ale może chcieć zrobić tak, aby upewnić się, że proces nie widzi pozostawionej zawartości pamięci z innych programy użytkowników. Instrukcje kodu maszynowego w programie zazwyczaj zakodowują przesunięcia 1000, 1004, 1008 dla dostępu do a, b i c - więc nie ma potrzeby przydzielania tych adresów w czasie wykonywania.

Dynamiczna alokacja pamięci różni się tym, że wskaźniki (słownie p_a, p_b, p_c) zostaną podane adresy w czasie kompilacji, jak opisany powyżej, ale dodatkowo:

  • wskazywanego do pamięci (każda z a , b i c) musi zostać znalezione w czasie wykonywania (zwykle, gdy funkcja statyczna zostanie najpierw uruchomiona, ale kompilator może to zrobić wcześniej, zgodnie z moim komentarzem do drugiej odpowiedzi), i
    • jeśli jest za mało pamięci w systemie operacyjnym, aby alokacja dynamiczna zakończyła się pomyślnie, biblioteka programu poprosi system operacyjny o więcej pamięci (np. stosując sbreak()) - który system operacyjny będzie typowo wymazać ze względów bezpieczeństwa
    • dynamiczne adresy dedykowanych dla każdego z a, b i c muszą być kopiowane z powrotem do wskaźników p_a, p_b i p_c.

To dynamiczne podejście jest wyraźnie bardziej zawiłe.

+0

Niezłe punkty. Przez "alokację pamięci w czasie kompilacji" masz na myśli, że wymagana przestrzeń pamięci jest zarezerwowana w czasie łącza i czasu ładowania, ale inicjalizacja jest odroczona do czasu wywołania funkcji? (W przeciwnym razie twój pierwszy punkt wydaje się błędny) –

+0

Właśnie zauważyłem [twoją ofertę] (http://stackoverflow.com/questions/15062767/heap-dynamic-vs-static-memory-allocation-for-c-singleton-class -instance/15063036 # comment21179656_15062905) z C++ 11 6.7.4. Ale co z C++ 03 lub wcześniejszymi wersjami? –

+0

@MassoodKhaari: Re alokacja: tak, decyzje dotyczące pamięci (wymagana ilość, segment, przesunięcia) dla statyki są dokonywane w czasie kompilacji, a obraz binarny będzie wskazywał na tyle na jej temat (np. Całkowity rozmiar obszaru pamięci) dla programu ładującego OS na odłóż pamięć na bok. Re współbieżność - jak na moją odpowiedź - jest to implementacja zdefiniowana (jeśli w ogóle) ... Standard C++ 03 w ogóle nie wspomniał o wątkach, więc to od decyzji zależy, czy i jak je wspierać. –

2

Główna różnica polega na tym, że przy użyciu lokalnego obiektu static obiekt zostanie zniszczony podczas zamykania programu, zamiast tego obiekty przydzielane sterty zostaną porzucone bez zniszczenia.

Należy zauważyć, że w C++, jeśli zadeklarujesz zmienną statyczną wewnątrz funkcji, zostanie ona zainicjowana przy pierwszym wejściu do zakresu, a nie przy uruchomieniu programu (jak to się dzieje zamiast globalnych statycznych zmiennych czasu trwania).

Generalnie przez lata przełączyłem się z używania leniwej inicjalizacji na jawnie kontrolowaną inicjalizację, ponieważ uruchamianie i zamykanie programów to delikatne fazy i dość trudne do debugowania. Jeśli twoja klasa nie robi nic skomplikowanego i po prostu nie może zawieść (np.to tylko rejestr), a nawet leniwy inicjalizacji jest w porządku ... w przeciwnym razie kontrola będzie zaoszczędzić sporo problemów.

Program, który ulega awarii przed wprowadzeniem pierwszej instrukcji main lub po wykonaniu ostatniej instrukcji main jest trudniej do debugowania.

Innym problemem związanym z używaniem leniwych konstrukcji singletonów jest to, że jeśli twój kod jest wielowątkowy, musisz zwrócić uwagę na ryzyko jednoczesnego zainicjowania wątków w tym samym czasie. Wykonanie inicjacji i wyłączenie w kontekście pojedynczego wątku jest prostsze.

+0

Tak, używam także singletonów dla raczej prostych obiektów. Ale wydaje się to interesujące; czy możesz wymienić kilka sposobów wdrożenia "jawnie kontrolowanej inicjalizacji"? –

+0

I miło wskazać na problem zniszczenia. Nie zauważyłem tego. Dlatego w tym przypadku inicjalizacja statyczna wydaje się lepszym wyborem. –

+0

"Zostanie zainicjalizowany przy pierwszym wejściu w zakres" - czasami/C++ 11 6.7.4: "Implementacja jest dozwolona do przeprowadzenia wczesnej inicjalizacji innych zmiennych o zakresie bloków z czasem trwania pamięci statycznej lub w tym samym czasie. warunki, w których implementacja jest dozwolona do statycznej inicjalizacji zmiennej czasu statycznego lub wątku w zakresie przestrzeni nazw (3.6.2), w przeciwnym razie taka zmienna jest zainicjowana po raz pierwszy kontrola przechodzi przez swoją deklarację, taka zmienna jest uważana za zainicjowaną po zakończenie jego inicjalizacji. " –

Powiązane problemy