2009-10-08 15 views
5

czytałem QT standardy kodowania docs i natknął się następujący akapit:C++ statyczny globalny non-POD: teoria i praktyka

niczego, co ma konstruktora lub musi uruchomić kod zostać zainicjowana nie może być używany jako obiekt globalny w kodzie biblioteki, ponieważ jest niezdefiniowany, gdy ten konstruktor/kod zostanie uruchomiony (przy pierwszym użyciu, przy ładowaniu biblioteki, przed głównym() lub wcale nie jest). Nawet jeśli czas inicjalizacji jest zdefiniowany dla bibliotek współdzielonych, będziesz mieć kłopoty z przeniesieniem kodu w wtyczce lub jeśli biblioteka zostanie skompilowana statycznie.

Wiem, co mówi theory, ale nie rozumiem części "wcale nie". Czasami używam globalnej statystyki statycznej non-POD (np. QString) i nigdy nie zdarzyło mi się, że mogą nie zostać zainicjowane ... Czy jest to specyficzne dla współużytkowanych obiektów/bibliotek DLL? Czy dzieje się tak tylko w przypadku uszkodzonych kompilatorów?

Co sądzisz o tej zasadzie?

+0

Twój QString może nie zostać zainicjowany, jeśli ich nie używasz. Ale jeśli ich użyjesz, zostaną zainicjowane. Nawet jeśli jest to tuż przed użyciem (tj. Tuż przed przywołaniem matury na obiekcie). –

Odpowiedz

8

Część "nie w ogóle" po prostu mówi, że standard C++ milczy na temat tego problemu. Nie wie o bibliotekach współdzielonych i dlatego nie mówi nic o interakcji niektórych funkcji C++ z tymi.

W praktyce widziałem globalne statyczne globalne nie-POD używane w Windows, OSX i wielu wersjach Linuksa i innych Uniksów, zarówno w GUI i programach wiersza poleceń, jako wtyczki i jako samodzielne aplikacje. Co najmniej jeden projekt (który używał globalnych statycznych non-POD) miał wersje dla pełnego zestawu wszystkich ich kombinacji. Jedyny problem, jaki kiedykolwiek widziałem, to to, że jakaś stara wersja GCC generowała kod, który zwany był dronami takich obiektów w bibliotekach dynamicznych, gdy plik wykonywalny został zatrzymany, a nie kiedy biblioteka została wyładowana. Oczywiście było to fatalne (kod biblioteki został wywołany, gdy biblioteka już zniknęła), ale to było prawie dziesięć lat temu.

Ale to oczywiście nie gwarantuje niczego.

2

Nie sądzę, że konstruktory obiektów statycznych mogą zostać usunięte. Prawdopodobnie istnieje nieporozumienie z faktem, że biblioteka statyczna jest często tylko garstką obiektów, które są tokenami w wykonywalnym sygnaturowaniu. Niektóre statyczne obiekty zostały zaprojektowane w taki sposób, że nie są przywoływane poza ich obiektem zawierającym, dlatego plik obiektowy jest umieszczany w pliku wykonywalnym tylko wtedy, gdy istnieje na nich inna zależność. Nie jest tak w przypadku niektórych wzorów (przy użyciu statycznego obiektu, który rejestruje się na przykład).

+0

Myślę, że mylicie "obiekt statyczny" z "biblioteką statyczną". – sbi

+1

Przez "obiekty statyczne" miałem na myśli "obiekty o statycznym czasie przechowywania". BTW, znalazłem referencję mówiąc, że nie można ich usunąć, jeśli ich inicjalizacja lub destruktor ma efekt uboczny: 3.7.1/2. – AProgrammer

+0

Miałem na myśli, że statyczne biblioteki najprawdopodobniej nie stanowią problemu. To są dynamiczne biblioteki, które są problemem. – sbi

2

Jeśli obiekt statyczny jest zdefiniowany w obiekcie, który nie jest przywoływany, łącznik może całkowicie wyczyścić obiekt, w tym statyczny kod inicjalizatora. Będzie to robić regularnie dla bibliotek libs (w ten sposób libc nie jest w pełni sprzężony podczas używania części pod GNU, np.).

Co ciekawe, nie sądzę, że jest to charakterystyczne dla bibliotek. Prawdopodobnie może się zdarzyć w przypadku obiektów nawet w głównej wersji.

2

Nie widzę problemu z posiadaniem obiektów globalnych z konstruktorami.

Nie powinny po prostu mieć żadnej zależności od innych obiektów globalnych w ich konstruktorze (lub destruktorze).

Ale jeśli mają one zależności, to obiekt zależny musi znajdować się w tej samej jednostce kompilacji lub leniwie oceniany, aby można było wymusić ocenę przed użyciem.

Kod w konstruktorze nie powinien być również zależny od tego, kiedy (jest to związane z zależnościami, ale nie do końca takie same) jest wykonywane, ale można bezpiecznie założyć, że zostanie on skonstruowany co najmniej (tuż przed metoda jest wywoływana), a C++ gwarantuje, że kolejność niszczenia jest odwrotnością instancji.

Nie jest trudno trzymać się tych zasad.

2

C++ nie definiuje kolejności wykonywania inicjalizatorów statycznych dla obiektów w różnych jednostkach kompilacji (porządek jest dobrze zdefiniowany w jednostce kompilacji).

Weź pod uwagę sytuację, w której masz 2 obiekty statyczne A i B zdefiniowane w różnych jednostkach kompilacji. Powiedzmy, że obiekt B faktycznie używa obiektu A w jego inicjalizacji.

W tym scenariuszu jest możliwe, że B zostanie najpierw zainicjowany i wykona połączenie z niezainicjowanym obiektem A. Może to być jedna rzecz, która jest rozumiana przez "nie w ogóle" - obiekt jest używany, gdy nie miał możliwości zainicjowania go na własną rękę (nawet jeśli może być zainicjowany później).

Przypuszczam, że dynamiczne łączenie może zwiększyć złożoność, o której nie pomyślałem, aby obiekt nie został zainicjowany. Tak czy inaczej, podstawowym założeniem jest to, że statyczne initializatino wprowadza wystarczająco dużo potencjalnych problemów, których powinno się unikać, jeśli to możliwe, i bardzo ostrożnie obchodzić się z nimi, gdzie trzeba z niego korzystać.