Jakiś czas temu natknąłem się na kod, który oznaczał zmienną składową klasy mutable
słowem kluczowym. O ile widzę, po prostu pozwala modyfikować zmienną w const
metodzie:
class Foo
{
private:
mutable bool done_;
public:
void doSomething() const { ...; done_ = true; }
};
Czy to jedyne użycie tego słowa kluczowego, czy jest w nim coś więcej niż na pierwszy rzut oka? Od tego czasu korzystałem z tej techniki na zajęciach, oznaczając boost::mutex
zmienną, pozwalającą const
funkcjom blokować ją ze względów bezpieczeństwa wątków, ale, szczerze mówiąc, wydaje się to trochę hack.
mutable
: stackoverflow.com/questions/15999123/...const
(typów), więc nie muszę tego robić:class A_mutable{}; using A = A_mutable const; mutable_t<A> a;
jeśli chcę domyślnie const, tj.mutable A a;
(Jawnie zmienny) iA a;
(domyślny const).Odpowiedzi:
Pozwala na rozróżnienie stałej bitowej i logicznej. Logiczna stała ma miejsce, gdy obiekt nie zmienia się w sposób widoczny przez interfejs publiczny, jak na przykład w przypadku blokowania. Innym przykładem może być klasa, która oblicza wartość przy pierwszym żądaniu i buforuje wynik.
Ponieważ c ++ 11
mutable
może być użyty na lambda do oznaczenia, że rzeczy przechwycone przez wartość są modyfikowalne (nie są domyślnie):źródło
x
w lambda pozostaje w lambda, tzn. Funkcja lambda może modyfikować tylko własną kopięx
. Zmiana nie jest widoczna na zewnątrz, oryginałx
pozostaje niezmieniony. Weź pod uwagę, że lambda są implementowane jako klasy funktorów; przechwycone zmienne odpowiadają zmiennym składowym.Słowo
mutable
kluczowe jest sposobem na przebicieconst
zasłony, którą zasłaniasz swoje przedmioty. Jeśli masz stałe odniesienie lub wskaźnik do obiektu, nie możesz modyfikować tego obiektu w żaden sposób, z wyjątkiem tego, kiedy i jak jest oznaczonymutable
.Z
const
odniesieniem lub wskaźnik jesteś ograniczony do:const
.mutable
Wyjątek sprawia, że tak można teraz pisać lub zestaw danych użytkowników, które są oznaczonemutable
. To jedyna widoczna z zewnątrz różnica.Wewnętrznie te
const
metody, które są widoczne, można również zapisywać do oznaczonych elementów danychmutable
. Zasadniczo konstelacja jest wszechstronnie przebita. Projektant interfejsu API musi całkowicie upewnić się, żemutable
nie niszczyconst
koncepcji i jest używany tylko w użytecznych specjalnych przypadkach. Słowomutable
kluczowe pomaga, ponieważ wyraźnie oznacza członków danych, którzy podlegają tym szczególnym przypadkom.W praktyce możesz używać
const
obsesyjnie w całej bazie kodu (zasadniczo chcesz „zainfekować” swoją bazę kodowąconst
„chorobą”). W tym świecie wskaźniki i referencje sąconst
z nielicznymi wyjątkami, dając kod łatwiejszy do zrozumienia i zrozumienia. W celu uzyskania interesującej dygresji wyszukaj „przejrzystość referencyjna”.Bez
mutable
słowa kluczowego będziesz w końcu zmuszony doconst_cast
obsługi różnych przydatnych specjalnych przypadków, na które pozwala (buforowanie, liczenie odwołań, dane debugowania itp.). Niestetyconst_cast
jest znacznie bardziej destrukcyjny niżmutable
dlatego, że zmusza klienta API do zniszczeniaconst
ochrony obiektów, których używa. Dodatkowo powoduje powszechneconst
zniszczenie:const_cast
wprowadzenie stałego wskaźnika lub odwołania umożliwia swobodny zapis i wywołanie metody dostępu do widocznych elementów. W przeciwieństwie do tegomutable
wymaga się od projektanta interfejsu API sprawowania drobiazgowej kontroli nadconst
wyjątkami i zwykle wyjątki te są ukryte wconst
metodach działających na danych prywatnych.(NB I odnoszą się do danych i sposób widoczności kilka razy. Mówię o członków oznaczonych jako prywatny lub publiczny w porównaniu z chronionymi, który jest zupełnie inny rodzaj ochrony obiektu omawiane tutaj ).
źródło
const_cast
do zmodyfikowania częściconst
obiektu daje niezdefiniowane zachowanie.const_cast
do implementacji mutacji zmiennych składowych wconst
metodzie, nie poprosiłbyś klienta o wykonanie rzutowania - zrobiłbyś to w ramach metody przezconst_cast
ingthis
. Zasadniczo pozwala to ominąć stałość dowolnych członków w określonej witrynie wywoławczej , a jednocześniemutable
pozwala usunąć stałą w określonym elemencie we wszystkich witrynach wywoławczych. To drugie jest zwykle tym, czego chcesz do typowego zastosowania (buforowanie, statystyki), ale czasami const_cast pasuje do wzorca.const_cast
Wzór pasuje lepiej w niektórych przypadkach, na przykład gdy chcesz tymczasowo zmodyfikować element, a następnie przywrócić je (prawie jakboost::mutex
). Metoda jest logicznie stała, ponieważ stan końcowy jest taki sam jak początkowy, ale chcesz wprowadzić tę przejściową zmianę.const_cast
może być przydatna, ponieważ pozwala odrzucić const w tej metodzie, gdyby mutacja została cofnięta, alemutable
nie byłaby odpowiednia, ponieważ usunęłaby const const ze wszystkich metod, które niekoniecznie wszystkie są zgodne z „do” , cofnij ”.const_cast
stworzyć bombę zegarową.mutable
nie ma takiego problemu, ponieważ takich obiektów nie można umieścić w pamięci tylko do odczytu.Twoje użycie z boost :: mutex jest dokładnie tym, do czego przeznaczone jest to słowo kluczowe. Innym zastosowaniem jest buforowanie wyników wewnętrznych w celu przyspieszenia dostępu.
Zasadniczo „zmienny” dotyczy dowolnego atrybutu klasy, który nie wpływa na zewnętrznie widoczny stan obiektu.
W przykładowym kodzie w pytaniu zmienna może być nieodpowiednia, jeśli wartość done_ wpływa na stan zewnętrzny, zależy to od tego, co znajduje się w ...; część.
źródło
Zmienna służy do oznaczania określonego atrybutu jako modyfikowalnego z poziomu
const
metod. To jest jego jedyny cel. Zastanów się, zanim go użyjesz, ponieważ kod będzie prawdopodobnie bardziej przejrzysty i czytelny, jeśli zmienisz projekt, a nie użyjeszmutable
.http://www.highprogrammer.com/alan/rants/mutable.html
Przykłady podane przez autora obejmują buforowanie i tymczasowe zmienne debugowania.
źródło
mutable
może sprawić, że kod będzie bardziej czytelny i przejrzysty. W poniższym przykładzie wartośćread
może byćconst
zgodna z oczekiwaniami. `mutable m_mutex; Pojemnik m_container; void add (Przedmiot pozycji) {Lockguard lock (m_mutex); m_container.pushback (item); } Element read () const {Lockguard lock (m_mutex); return m_container.first (); } `Jest to przydatne w sytuacjach, w których masz ukryty stan wewnętrzny, taki jak pamięć podręczna. Na przykład:
I wtedy możesz mieć
const HashTable
obiekt nadal korzystający z jegolookup()
metody, która modyfikuje wewnętrzną pamięć podręczną.źródło
mutable
istnieje, gdy wnioskujesz, aby umożliwić modyfikowanie danych w funkcji stałej w przeciwnym razie.Chodzi o to, abyś mógł mieć funkcję, która „nic nie robi” wewnętrznemu stanowi obiektu, więc oznaczysz tę funkcję
const
, ale naprawdę może być konieczna modyfikacja niektórych stanów obiektów w sposób, który nie wpływa na jej poprawność funkcjonalność.Słowo kluczowe może działać jako wskazówka dla kompilatora - teoretyczny kompilator może umieścić stały obiekt (taki jak globalny) w pamięci, który został oznaczony jako tylko do odczytu. Obecność
mutable
wskazówek, że nie należy tego robić.Oto kilka ważnych powodów, aby zadeklarować i użyć danych podlegających zmianom:
mutable boost::mutex
jest całkowicie uzasadniona.źródło
const
(a taka kontrola zakończy się powodzeniem lub niepowodzeniem niezależnie odconst
lubmutable
). Samo zadeklarowanie funkcjiconst
nie wystarczy:const
funkcja może mieć skutki uboczne, takie jak modyfikacja zmiennej globalnej lub coś przekazanego do funkcji, więc nie jest to przydatna gwarancja dla tego dowodu.const
słowem kluczowym w C ++.Tak, właśnie to robi. Używam go dla członków zmodyfikowanych metodami, które nie logicznie zmieniają stanu klasy - na przykład w celu przyspieszenia wyszukiwania poprzez implementację pamięci podręcznej:
Teraz musisz używać tego ostrożnie - problemy z współbieżnością są dużym problemem, ponieważ osoba dzwoniąca może założyć, że jest bezpieczna dla wątków, jeśli używa tylko
const
metod. I oczywiście modyfikowaniemutable
danych nie powinno w żaden znaczący sposób wpływać na zachowanie obiektu, co może zostać naruszone w podanym przeze mnie przykładzie, jeśli na przykład spodziewano się, że zmiany zapisane na dysku będą natychmiast widoczne dla aplikacji .źródło
Zmienna jest używana, gdy masz zmienną wewnątrz klasy, która jest używana tylko w tej klasie do sygnalizowania rzeczy takich jak na przykład muteks lub blokada. Ta zmienna nie zmienia zachowania klasy, ale jest niezbędna do wdrożenia bezpieczeństwa wątków samej klasy. Zatem bez „mutable” nie byłoby możliwe posiadanie funkcji „const”, ponieważ zmienna ta będzie musiała zostać zmieniona we wszystkich funkcjach dostępnych dla świata zewnętrznego. Dlatego zmienna została wprowadzona, aby zmienna składowa była zapisywalna nawet przez funkcję const.
źródło
Zmienna jest używana głównie w szczegółach implementacji klasy. Użytkownik klasy nie musi o tym wiedzieć, dlatego metoda, którą uważa, że „powinna” być stała, może być. Twój przykład muteksa, który można mutować, jest dobrym przykładem kanonicznym.
źródło
Używanie go nie jest hackowaniem, chociaż jak wiele rzeczy w C ++, mutable może być hackiem dla leniwego programisty, który nie chce cofać się i oznaczać czegoś, co nie powinno być const jako non-const.
źródło
Użyj „mutable”, gdy dla rzeczy, które są logicznie bezstanowe dla użytkownika (a zatem powinny mieć „const” pobierające w interfejsach API klasy publicznej), ale NIE są bezstanowe w podstawowej IMPLEMENTACJI (kod w twoim .cpp).
Przypadki, których najczęściej używam, to leniwa inicjalizacja niepaństwowych członków „zwykłych starych danych”. Mianowicie jest idealny w wąskich przypadkach, gdy takie elementy są drogie albo do zbudowania (procesor), albo do noszenia (pamięć), a wielu użytkowników obiektu nigdy o nie nie poprosi. W tej sytuacji chcesz leniwej konstrukcji na zapleczu dla wydajności, ponieważ 90% zbudowanych obiektów nigdy nie będzie musiało ich wcale budować, a mimo to musisz przedstawić poprawny bezstanowy interfejs API do publicznego użytku.
źródło
Zmienna zmienia znaczenie
const
z stałej bitowej na stałą logiczną dla klasy.Oznacza to, że klasy ze zmiennymi elementami są już bitami const i nie będą już pojawiać się w sekcjach pliku wykonywalnego tylko do odczytu.
Co więcej, modyfikuje sprawdzanie typu, pozwalając
const
funkcjom członkowskim zmieniać zmienne elementy bez użyciaconst_cast
.Zobacz inne odpowiedzi, aby uzyskać więcej szczegółów, ale chciałem podkreślić, że nie chodzi tylko o bezpieczeństwo typu i że wpływa to na skompilowany wynik.
źródło
W niektórych przypadkach (np. Źle zaprojektowane iteratory) klasa musi zachować liczbę lub inną przypadkową wartość, która tak naprawdę nie wpływa na główny „stan” klasy. To jest najczęściej tam, gdzie używam mutable. Bez możliwości modyfikacji będziesz musiał poświęcić całą trwałość swojego projektu.
Dla mnie to też jest hack. Przydatny w bardzo niewielu sytuacjach.
źródło
Klasyczny przykład (jak wspomniano w innych odpowiedziach) i jedyna sytuacja, w której do tej pory widziałem
mutable
słowo kluczowe, dotyczy buforowania wyniku skomplikowanejGet
metody, w której pamięć podręczna jest implementowana jako element danych klasy, a nie jako zmienna statyczna w metodzie (ze względu na współdzielenie kilku funkcji lub zwykłą czystość).Zasadniczo alternatywami dla użycia
mutable
słowa kluczowego są zwykle zmienna statyczna w metodzie lubconst_cast
sztuczce.Inne szczegółowe wyjaśnienie znajduje się tutaj .
źródło
const_cast
to tylko wtedy, gdy wiesz (lub masz gwarancję), że coś się nie zmieni (np. Podczas ingerowania w biblioteki C) lub gdy wiesz, że nie zostało to zadeklarowane jako const. Tzn. Modyfikacja stałej const rzutowanej powoduje w wyniku niezdefiniowane zachowanie.const_cast
może być użyty do modyfikacji członka klasy wconst
metodzie, o czym mówiłem ...const_cast
, jak powiedziano, jest to dozwolone tylko wtedy, gdy obiekt nie został zadeklarowanyconst
. Na przykładconst Frob f; f.something();
, zvoid something() const { const_cast<int&>(m_foo) = 2;
wynikami w niezdefiniowanej zachowań.Zmienna może być przydatna, gdy przesłonisz stałą funkcję wirtualną i chcesz zmodyfikować zmienną członka klasy podrzędnej w tej funkcji. W większości przypadków nie chciałbyś zmieniać interfejsu klasy podstawowej, więc musisz użyć własnej zmiennej zmiennej składowej.
źródło
Zmienne słowo kluczowe jest bardzo przydatne podczas tworzenia kodów pośredniczących na potrzeby testów klasowych. Możesz podstawić funkcję const i nadal być w stanie zwiększyć (modyfikować) liczniki lub dowolną funkcjonalność testową, którą dodałeś do swojego kodu pośredniczącego. Utrzymuje to interfejs nienaruszonej klasy.
źródło
Jednym z najlepszych przykładów, w których używamy mutable, jest głębokie kopiowanie. w konstruktorze kopii wysyłamy
const &obj
jako argument. Tak więc utworzony nowy obiekt będzie typu stałego. Jeśli chcemy zmienić (przeważnie tego nie zmienimy, w rzadkich przypadkach możemy zmienić) członkowie tego nowo utworzonego obiektu const musimy zadeklarować jakomutable
.mutable
klasa pamięci może być używana tylko na niestatycznym, stałym obiekcie danych należącym do klasy. Zmienny element danych klasy może być modyfikowany, nawet jeśli jest częścią obiektu zadeklarowanego jako const.W powyższym przykładzie jesteśmy w stanie zmienić wartość zmiennej składowej
x
składowej, chociaż jest ona częścią obiektu zadeklarowanego jako const. Wynika to z faktu, że zmiennax
jest zadeklarowana jako zmienna . Ale jeśli spróbujesz zmodyfikować wartość zmiennejy
składowej, kompilator zgłosi błąd.źródło
Samo słowo kluczowe „mutable” jest faktycznie słowem kluczowym zastrzeżonym. Często jest używane do zmiany wartości zmiennej stałej. Jeśli chcesz mieć wiele wartości constsnt, użyj słowa kluczowego mutable.
źródło