2014-10-24 13 views
8

Załóżmy, że mam klasę Foo że posiada prywatny wskaźnik do Bar:Czy dopuszczalne jest odrzucenie constness w konstruktorze ruchu?

class Foo 
{ 
private: 
    Bar * bar; 

public: 
    Foo() : bar (new Bar()) 
    {} 

    ~Foo() 
    { 
    delete bar; 
    } 
}; 

Jeśli wskaźnik bar nigdy nie powinien być przeniesiony do innej instancji, to ma sens, aby ten sam wskaźnik const żeby mnie zatrzymać (lub opiekunowie) od tego w przyszłości:

private: 
    Bar * const bar; 

Lubię robić to gdzie nadarzy się okazja.

Gdybym wtedy chciałem napisać konstruktora poruszać, będzie to wyglądać mniej więcej tak:

Foo (Foo && f) : 
    bar (f.bar) 
{ 
    f.bar = NULL; // uh oh; f.bar is const. 
} 

mogę „make błąd odejść” albo przez odlewanie z dala constness z f.bar lub nie czyni go const w pierwszej kolejności. Żadne z tych rzeczy nie chcę robić. Wolałbym nie usunąć całkowicie, ponieważ jest z jakiegoś powodu. Z drugiej strony, rzucanie dzwonów alarmowych dla mnie jest czymś, czego zwykle nie robię. Moje pytanie brzmi: czy uważa się za akceptowalną praktykę odrzucenie konsternacji w konstruktorze ruchu takim jak ten? Czy istnieje lepszy sposób, którego nie rozważałem?

nie sądzę, moje pytanie jest taki sam jak ten: Use const_cast to implement the move constructor

+7

Jeśli chcesz zmienić 'bar', to całkiem wyraźnie nie powinno to być' const'. Pomyśl dokładnie o swoim projekcie. – user657267

+0

Wygląda na to, że twoja klasa nie powinna być ruchoma. – juanchopanza

+2

Użycie const_cast tutaj jest niezdefiniowanym zachowaniem. –

Odpowiedz

6

Jeśli będziesz musiał zmienić wskaźnik w niektórych przypadkach (nawet jeśli jest to tylko pojedynczy przypadek), to prawdopodobnie nie powinien on być const.

Jednak nawet odłożenie tej myśli, używając const_cast, aby usunąć constness z obiektu, a następnie używając wyniku rzutowania, wywołuje niezdefiniowane zachowanie. Jest tylko bezpieczna dla const_cast zmienną, która pierwotnie nie była stała.

+1

Dokładniej, nieokreślonym zachowaniem jest zapisywanie wyniku rzutu. Możesz nadal czytać z wyniku. –

+0

Sądzę, że ta lekcja to JEDNA RZECIA, której nauczyłem się w tym roku na temat poprawnego lub nieokreślonego wywołania zachowania const_cast: "Nigdy nie modyfikuj zmiennej, której deklaracja jest stała". – paercebal

4

Jesteś w zasadzie sprzeczne siebie.
Mówisz: "Nie chcę tego zmieniać w żadnych okolicznościach", a ty mówisz "Chcę to zmienić, gdy ruszam obiektem".

Tak więc, jeśli istnieje scenariusz, w którym należy go zmienić, to nie jest to const i można go wygodnie usunąć.

Jeśli jesteś zdecydowany, że chcesz go const, możesz dodać kolejną wartość boolowską, która określa własność (jeśli chcesz obsłużyć okres eksploatacji zasobu). Tak więc wskazany obiekt może zostać zwolniony tylko wtedy, gdy wywoływany jest destruktor będący właścicielem obiektu.

+0

Zgadzam się, że sam sobie zaprzeczam. Nie chcę, aby to się zmieniło w żadnych okolicznościach, z wyjątkiem konstruktora ruchu. Przypuszczam, że to jest szkoda, że ​​TYLKO konstruktor ruchu powstrzyma mnie przed używaniem const. – peterpi

+2

Pokazuje, że Twój projekt ma wadę. Istnieje przypadek, w którym wskaźnik ten można zmienić. Przykład: '{Foo f; Foo f2 (std :: move (f)); f.somefn(); } ' Nic nie jest nie w porządku z tym fragmentem kodu, ale somefn() miało polegać na niezmienionej wartości paska. Bardzo wyraźnie powiedziałeś kompilatorowi (i przyszłym czytelnikom twojego kodu), że gdy tylko pasek zostanie zainicjowany, zmieni się on zawsze. Ale ponieważ obiekt został przeniesiony - z niego się zmienił. W ten sposób naruszając konstytucję członka. –

+0

@AndreKostur To wspaniałe wyjaśnienie, dziękuję. Pokazuje to, że "dodanie ruchomości" to więcej niż dodanie konstruktora ruchu. We wszystkich funkcjach składowych należy rozważyć zachowanie obiektu * post move *. – peterpi

3

Jeśli to możliwe, należy unique_ptr zamiast:

std::unique_ptr<Bar> bar; 
Foo(Foo&& f) : bar(std::move(f.bar)){} 
// or maybe you won't even have to declare move constructor 

W ten sposób nie tylko dostaniesz większe bezpieczeństwo i komfort, ale także nie będzie przypadkowo przepisać go z =, będziesz musiał zadzwonić .reset() więc pomyśl dwa razy zanim to zrobisz (jak przypuszczam).

Powiązane problemy