2015-01-19 15 views
22

mam kodu C++, który sprowadza się do czegoś, co następuje:inicjować zmienne składowe const

class Foo{ 
    bool bar; 
    bool baz; 
    Foo(const void*); 
}; 
Foo::Foo(const void* ptr){ 
    const struct my_struct* s = complex_method(ptr); 
    bar = calculate_bar(s); 
    baz = calculate_baz(s); 
} 

Semantycznie, bar i BAZ zmienne składowe powinny być const, gdyż nie powinno się zmienić po inicjalizacji. Wydaje się jednak, że aby tak się stało, musiałbym zainicjować je na liście inicjującej, a nie przypisywać. Aby było jasne, rozumiem, dlaczego muszę to zrobić. Problem polega na tym, że nie może się znaleźć jakiś sposób na konwersję kodu do listy inicjalizacji nie robiąc jedną z następujących niepożądanych rzeczy:

  • połączenia complex_method dwukrotnie (byłoby złe dla wydajności)
  • Add wskaźnik do klasy Foo (spowodowałby niepotrzebnie duży rozmiar klasy)

Czy istnieje jakiś sposób, aby ustawić zmienne, unikając tych niepożądanych sytuacji?

+0

Na marginesie, jesteś pewien, że 'complex_method' powinien wrócić przez wskaźnik? A jeśli tak, to nie musisz go czyścić, co sugeruje użycie 'std :: unique_ptr', aby go zapisać? – Deduplicator

+0

W porządku, właśnie uprościłem rzeczywisty kod powyższego fragmentu, aby uzyskać zwięzłość. – dooglius

+2

Unikaj tworzenia stałych członków. Jeśli obiekt Foo nie powinien się zmienić, zamiast tego należy ustawić rzeczywisty obiekt const. –

Odpowiedz

25

Jeśli możesz sobie pozwolić na kompilator C++ 11, należy rozważyć delegating constructors:

class Foo 
{ 
    // ... 
    bool const bar; 
    bool const baz; 
    Foo(void const*); 
    // ... 
    Foo(my_struct const* s); // Possibly private 
}; 

Foo::Foo(void const* ptr) 
    : Foo{complex_method(ptr)} 
{ 
} 

// ... 

Foo::Foo(my_struct const* s) 
    : bar{calculate_bar(s)} 
    , baz{calculate_baz(s)} 
{ 
} 

Jako ogólną poradę, należy uważać, deklarowanie członków danych jako const, ponieważ to uniemożliwia skopiowanie twojej klasy - przypisanie i przeniesienie - przypisanie. Jeśli twoja klasa ma być używana z semantyką wartości, te operacje stają się pożądane. Jeśli tak nie jest, możesz zignorować tę notatkę.

+2

UV, szczególnie w przypadku notatki na końcu. – Angew

8

Można użyć konstruktora delegata w C++ 11:

class Foo{ 
public: 
    Foo(const void* ptr) : Foo(complex_method(ptr)) {} 

private: 
    Foo(const my_struct* s) : bar(calculate_bar(s)), baz(calculate_baz(s)) {} 

private: 
    const bool bar; 
    const bool baz; 
}; 
11

Jedną z opcji jest konstruktor delegujący C++ 11, jak omówiono w innych odpowiedziach. Metoda 03-kompatybilny C++ jest użycie podobiekt:

class Foo{ 
    struct subobject { 
     const bool bar; 
     const bool baz; 
     subobject(const struct my_struct* s) 
      : bar(calculate_bar(s)) 
      , baz(calculate_baz(s)) 
     {} 
    } subobject; 
    Foo(const void*); 
}; 
Foo::Foo(const void* ptr) 
    : subobject(complex_method(ptr)) 
{} 

Można dokonać bar i baz const lub dokonać subobject const, lub obu.

Jeśli się tylko subobject const, wówczas można obliczyć complex_method i przypisać do bar i baz wewnątrz konstruktora subobject:

class Foo{ 
    const struct subobject { 
     bool bar; 
     bool baz; 
     subobject(const void*); 
    } subobject; 
    Foo(const void*); 
}; 
Foo::Foo(const void* ptr) 
    : subobject(ptr) 
{} 
Foo::subobject::subobject(const void* ptr){ 
    const struct my_struct* s = complex_method(ptr); 
    bar = calculate_bar(s); 
    baz = calculate_baz(s); 
} 

powodu że nie można mutować const członków w ciele konstruktora jest to, że korpus konstruktora jest traktowany tak jak każdy inny element członkowski, dla zachowania spójności. Zauważ, że możesz przenieść kod z konstruktora do funkcji składowej dla refaktoryzacji, a zrezygnowana funkcja składowa nie wymaga specjalnej obróbki.

2

Jeśli nie chcesz używać konstruktorów delegujących newfangled (nadal mam do czynienia z wersjami kompilatorów, które o nich nie wiedzą), a nie chcesz zmieniać układu swojej klasy, możesz wybierz rozwiązanie, które zastępuje konstruktor argumentem const void * przez funkcję statycznego elementu zwracającą Foo, mając prywatny konstruktor, który pobiera dane wyjściowe z complex_method jako argument (ten ostatni, podobnie jak przykłady konstruktora delegującego).Statyczna funkcja członka wykonuje niezbędne wstępne obliczenia z udziałem complex_method i kończy się na return Foo(s);. Wymaga to, aby klasa miała dostępny konstruktor kopii, mimo że jego wywołanie (w instrukcji return) może najprawdopodobniej zostać usunięte.

Powiązane problemy