2013-02-19 16 views
6

Przepisuję trochę kodu, aby wyeliminować zmienne globalne i uczyniłem funkcję konstruktora klasy/destruktora w celu czyszczenia niektórych zasobów biblioteki stron trzecich, ale niepokoi mnie kod inicjujący jednego członka z innego elementu na liście inicjatora klasy.Czy mogę używać członków klasy C++ zainicjowanych na liście inicjalizacyjnej, a później na liście?

class MyPodofoDocument { 
public: 
    // generates pdf to stream 
    MyPodofoDocument(std::stringstream *pStringStream) 
     : device(pStringStream), document(&device) 
    { 
    } 
private: 
    PoDoFo::PdfOutputDevice device; 
    PoDoFo::PdfStreamedDocument document; 
    PoDoFo::PdfPainter painter; 
}; 

Kod, który korzysta z tej klasy nie muszą zobaczyć wszystkie szczegóły, które go do korzystania z biblioteki, ale sposób w jaki je ukryć sprawia, że ​​uzależnieni od członków zainicjować innych członków, zanim dotrze on do rzeczywisty blok kodu konstruktora, gdzie ma poprawny ten wskaźnik.

Działa w jednostce szkieletu testowego, więc moje pytanie brzmi: "Czy to jest w porządku, przenośne i bezpieczne?"

Odpowiedz

8

Członkowie są inicjowane w kolejności są zadeklarowane, od góry do dołu

PoDoFo::PdfOutputDevice device; 
PoDoFo::PdfStreamedDocument document; 
PoDoFo::PdfPainter painter; 

więc jest bezpieczny w użyciu device zainicjować document.

+0

Ponadto legalne jest uzyskanie adresu lub powiązanie odniesienia z jeszcze nieodbudowanym członkiem (tzn. Można przekazać referencję do członka zadeklarowanego później, jeśli odbiorca nie * używa * obiektu, ale przechowuje jedynie referencję/wskaźnik). –

+0

Jest legalne, ale semantycznie błędne, ponieważ przekazujesz wskaźnik do czegoś, co nie zostało jeszcze skonstruowane. –

+0

@AlexChamberlain: Nie ma w tym nic złego, ale prawdopodobnie potrzebna jest podwójna kontrola. – GManNickG

4

Rodzaj. Reguły są takie, że zmienne składowe są inicjowane w kolejności, w jakiej są zadeklarowane w deklaracji klasy.

W twoim przypadku jest w porządku, ponieważ device jest zadeklarowane przed document.

Jednak w następującym przypadku mamy niezdefiniowane zachowanie, pomimo porządku listy inicjalizatorów.

class A { 
public: 
    A(int i) : b(i), a(b) { } 
private: 
    int a; 
    int b; 
} 
+1

Mimo że jest bezpieczny *, dopóki zależności między zmiennymi są takie same jak w zamówieniu *, IMHO nie jest to bardzo dobra praktyka. Taki kod powinien być unikany ilekroć jest to możliwe, tylko dlatego, że zbyt łatwo można błędnie modyfikować kolejność deklaracji podczas refaktoryzacji klasy, uruchamiając UB, jak pokazuje to przykład Alexa. Innymi słowy, jest to poprawne, ale bardzo kruche. – syam

+1

@syam: Nie jestem zbyt pewny ... Kiedyś myślałem tak samo, ale bardzo łatwo jest zmusić kompilator do ostrzeżenia, jeśli inicjatory są nieczynne (kompilator zwróci twoją uwagę na potencjalny UB) w niektórych przypadkach pomocne może być odwoływanie się do innego członka tego samego typu. –

+0

Jest to przydatne nie tylko dla rzeczy tego samego typu; cokolwiek, gdzie istnieje prawdziwa zależność między zmiennymi, ale gdzie należy również przechowywać zmienne. –

Powiązane problemy