Vor einiger Zeit bin ich auf Code gestoßen, der eine Mitgliedsvariable einer Klasse mit dem mutable
Schlüsselwort markiert . Soweit ich sehen kann, können Sie damit einfach eine Variable in einer const
Methode ändern :
class Foo
{
private:
mutable bool done_;
public:
void doSomething() const { ...; done_ = true; }
};
Ist dies die einzige Verwendung dieses Schlüsselworts oder steckt mehr dahinter, als man denkt? Ich habe diese Technik seitdem in einer Klasse verwendet und eine boost::mutex
als veränderlich markiert, damit const
Funktionen sie aus Gründen der Thread-Sicherheit sperren können, aber um ehrlich zu sein, fühlt es sich wie ein Hack an.
mutable
: stackoverflow.com/questions/15999123/…const
(von Typen) verwendet werden, damit ich dies nicht tun muss:class A_mutable{}; using A = A_mutable const; mutable_t<A> a;
Wenn ich standardmäßig const möchte, dhmutable A a;
(explizit veränderbar) undA a;
(implizite const).Antworten:
Es ermöglicht die Unterscheidung von bitweiser Konstante und logischer Konstante. Logische Konstante ist, wenn sich ein Objekt nicht auf eine Weise ändert, die über die öffentliche Schnittstelle sichtbar ist, wie in Ihrem Sperrbeispiel. Ein anderes Beispiel wäre eine Klasse, die beim ersten Anfordern einen Wert berechnet und das Ergebnis zwischenspeichert.
Da c ++ 11
mutable
auf einem Lambda verwendet werden kann, um anzuzeigen, dass durch Wert erfasste Dinge modifizierbar sind (sie sind nicht standardmäßig):quelle
x
im Lambda modifizierte im Lambda bleibt, dh die Lambda-Funktion kann nur ihre eigene Kopie von modifizierenx
. Die Änderung ist außen nicht sichtbar, das Originalx
bleibt unverändert. Bedenken Sie, dass Lambdas als Funktorklassen implementiert sind. erfasste Variablen entsprechen Mitgliedsvariablen.Das
mutable
Schlüsselwort ist eine Möglichkeit, denconst
Schleier zu durchbohren, den Sie über Ihre Objekte legen. Wenn Sie eine konstante Referenz oder Zeiger auf ein Objekt haben, können Sie dieses Objekt nicht in irgendeiner Weise ändern , außer , wann und wie es markiert istmutable
.Mit Ihrer
const
Referenz oder Ihrem Zeiger sind Sie beschränkt auf:const
.Mit der
mutable
Ausnahme können Sie jetzt markierte Datenelemente schreiben oder festlegenmutable
. Das ist der einzige äußerlich sichtbare Unterschied.Intern können die
const
für Sie sichtbaren Methoden auch in markierte Datenelemente schreibenmutable
. Im Wesentlichen wird der konstante Schleier umfassend durchbohrt. Es liegt ganz beim API-Designer, sicherzustellen, dassmutable
dasconst
Konzept nicht zerstört wird und nur in nützlichen Sonderfällen verwendet wird. Dasmutable
Schlüsselwort hilft, da es Datenelemente, die diesen Sonderfällen unterliegen, eindeutig kennzeichnet.In der Praxis können Sie
const
Ihre Codebasis zwanghaft verwenden (Sie möchten Ihre Codebasis im Wesentlichen mit derconst
"Krankheit" "infizieren "). In dieser Welt sind Zeiger und Referenzenconst
mit sehr wenigen Ausnahmen , die Code liefern, der leichter zu verstehen und zu verstehen ist. Für einen interessanten Exkurs schauen Sie nach "referentielle Transparenz".Ohne das
mutable
Schlüsselwort werden Sie möglicherweise gezwungen sein,const_cast
die verschiedenen nützlichen Sonderfälle zu behandeln, die es zulässt (Caching, Ref-Counting, Debug-Daten usw.). Leiderconst_cast
ist es wesentlich destruktiver alsmutable
weil es den API- Client zwingt , denconst
Schutz der von ihm verwendeten Objekte zu zerstören . Darüber hinaus führt dies zu einer weit verbreitetenconst
Zerstörung:const_cast
Wenn Sie einen konstanten Zeiger oder eine Referenz verwenden, können Sie uneingeschränkt schreiben und Methoden aufrufen, um auf sichtbare Elemente zuzugreifen. Im Gegensatz dazumutable
muss der API-Designer dieconst
Ausnahmen genau steuern. In der Regel sind diese Ausnahmen inconst
Methoden verborgen , die mit privaten Daten arbeiten.(Hinweis: Ich beziehe mich einige Male auf die Sichtbarkeit von Daten und Methoden . Ich spreche von Mitgliedern, die als öffentlich oder privat oder geschützt gekennzeichnet sind. Dies ist eine völlig andere Art des hier diskutierten Objektschutzes .)
quelle
const_cast
einem Teil eines modifizierenconst
ergibt Objekt nicht definiertes Verhalten.const_cast
Mutation von Mitgliedsvariablen in einerconst
Methode implementieren würden, würden Sie den Client nicht bitten, die Umwandlung durchzuführen - Sie würden dies innerhalb der Methode tun , indem Sieconst_cast
ingthis
. Grundsätzlich können Sie die Konstanz für beliebige Mitglieder an einer bestimmten Anrufstelle umgehen , währendmutable
Sie die Konstanz für ein bestimmtes Mitglied an allen Anrufstellen entfernen können . Letzteres ist normalerweise das, was Sie für die typische Verwendung wünschen (Caching, Statistiken), aber manchmal passt der const_cast zum Muster.const_cast
Muster passt in einigen Fällen besser, z. B. wenn Sie ein Mitglied vorübergehend ändern und dann wiederherstellen möchten (ähnlich wieboost::mutex
). Die Methode ist logisch konstant, da der Endzustand mit dem Anfangszustand identisch ist, Sie diese vorübergehende Änderung jedoch vornehmen möchten.const_cast
kann dort nützlich sein, weil Sie damit const speziell in dieser Methode wegwerfen können, wenn die Mutation rückgängig gemacht wird, abermutable
nicht so angemessen wäre, da dadurch der const-Schutz von allen Methoden entfernt würde, die nicht unbedingt alle dem "do" folgen , "Muster rückgängig machen".const_cast
eine mögliche Zeitbombe möglich.mutable
hat kein solches Problem, da solche Objekte nicht im Nur-Lese-Speicher abgelegt werden konnten.Ihre Verwendung mit boost :: mutex ist genau das, wofür dieses Schlüsselwort bestimmt ist. Eine andere Verwendung ist das interne Zwischenspeichern von Ergebnissen, um den Zugriff zu beschleunigen.
Grundsätzlich gilt "veränderlich" für alle Klassenattribute, die den extern sichtbaren Zustand des Objekts nicht beeinflussen.
In dem Beispielcode in Ihrer Frage ist veränderlich möglicherweise unangemessen, wenn der Wert von done_ den externen Status beeinflusst. Dies hängt davon ab, was in ... enthalten ist. Teil.
quelle
Mutable dient zum Markieren eines bestimmten Attributs als innerhalb von
const
Methoden veränderbar. Das ist der einzige Zweck. Überlegen Sie sorgfältig, bevor Sie es verwenden, da Ihr Code wahrscheinlich sauberer und lesbarer ist, wenn Sie das Design ändern, anstatt es zu verwendenmutable
.http://www.highprogrammer.com/alan/rants/mutable.html
Beispiele, die der Autor angibt, sind Caching- und temporäre Debugging-Variablen.
quelle
mutable
kann den Code lesbarer und sauberer machen. Im folgenden Beispielread
kann dasconst
wie erwartet sein. `veränderlicher m_mutex; Container m_container; void add (Item item) {Lockguard lock (m_mutex); m_container.pushback (item); } Item read () const {Lockguard lock (m_mutex); return m_container.first (); } `Dies ist nützlich in Situationen, in denen Sie den internen Status ausgeblendet haben, z. B. in einem Cache. Zum Beispiel:
Und dann kann ein
const HashTable
Objekt weiterhin seinelookup()
Methode verwenden, die den internen Cache ändert.quelle
mutable
existiert, wenn Sie daraus schließen, dass man Daten in einer ansonsten konstanten Funktion ändern kann.Die Absicht ist, dass Sie möglicherweise eine Funktion haben, die "nichts" mit dem internen Status des Objekts tut, und Sie die Funktion markieren
const
, aber Sie müssen möglicherweise einige der Objektzustände so ändern, dass sie sich nicht auf den korrekten Status auswirken Funktionalität.Das Schlüsselwort kann als Hinweis für den Compiler dienen. Ein theoretischer Compiler kann ein konstantes Objekt (z. B. ein globales Objekt) im Speicher ablegen, das als schreibgeschützt markiert wurde. Das Vorhandensein von
mutable
Hinweisen, dass dies nicht getan werden sollte.Hier einige gültige Gründe, um veränderbare Daten zu deklarieren und zu verwenden:
mutable boost::mutex
ist völlig vernünftig.quelle
const
(und eine solche Überprüfung wird unabhängig vonconst
oder erfolgreich sein oder fehlschlagenmutable
). Esconst
reicht nicht aus , nur die Funktion zu deklarieren : Eineconst
Funktion kann Nebenwirkungen haben, z. B. das Ändern einer globalen Variablen oder etwas, das an die Funktion übergeben wird. Daher ist dies keine nützliche Garantie für diesen Beweis.const
Schlüsselwort in C ++ zusammen.Nun ja, das ist es, was es tut. Ich verwende es für Mitglieder, die durch Methoden geändert werden, die den Status einer Klasse nicht logisch ändern - zum Beispiel, um die Suche durch Implementierung eines Caches zu beschleunigen:
Jetzt müssen Sie dies mit Vorsicht verwenden - Parallelitätsprobleme sind ein großes Problem, da ein Aufrufer möglicherweise davon ausgeht, dass sie threadsicher sind, wenn nur
const
Methoden verwendet werden. Und natürlich sollte das Ändern vonmutable
Daten das Verhalten des Objekts nicht wesentlich verändern. Dies könnte durch das von mir angegebene Beispiel verletzt werden, wenn beispielsweise erwartet würde, dass auf die Festplatte geschriebene Änderungen für die App sofort sichtbar sind .quelle
Mutable wird verwendet, wenn Sie eine Variable innerhalb der Klasse haben, die nur innerhalb dieser Klasse verwendet wird, um Dinge wie beispielsweise einen Mutex oder eine Sperre zu signalisieren. Diese Variable ändert das Verhalten der Klasse nicht, ist jedoch erforderlich, um die Thread-Sicherheit der Klasse selbst zu implementieren. Ohne "veränderlich" könnten Sie also keine "const" -Funktionen haben, da diese Variable in allen Funktionen geändert werden muss, die der Außenwelt zur Verfügung stehen. Daher wurde mutable eingeführt, um eine Mitgliedsvariable auch durch eine const-Funktion beschreibbar zu machen.
quelle
mutable wird hauptsächlich für ein Implementierungsdetail der Klasse verwendet. Der Benutzer der Klasse muss nichts darüber wissen, daher ist die Methode, die er für "sollte" hält, const. Ihr Beispiel dafür, dass ein Mutex veränderlich ist, ist ein gutes kanonisches Beispiel.
quelle
Ihre Verwendung ist es nicht ein Hack, obwohl wie viele Dinge in C ++, wandelbar kann Hack für einen faulen Programmierer sein , wer möchte nicht den ganzen Weg zurück und markieren Sie etwas gehen , die nicht const als nicht-const sein sollte.
quelle
Verwenden Sie "veränderlich", wenn für Dinge, die für den Benutzer LOGISCH zustandslos sind (und daher "const" -Getter in den APIs der öffentlichen Klasse enthalten sollten), in der zugrunde liegenden IMPLEMENTATION (dem Code in Ihrer .cpp) jedoch NICHT zustandslos sind.
Die Fälle, in denen ich es am häufigsten verwende, sind die verzögerte Initialisierung zustandsloser "einfacher alter Daten" -Mitglieder. Es ist nämlich ideal in engen Fällen, in denen es teuer ist, solche Mitglieder entweder zu bauen (Prozessor) oder herumzutragen (Speicher), und viele Benutzer des Objekts niemals nach ihnen fragen werden. In dieser Situation möchten Sie eine verzögerte Konstruktion im Back-End für die Leistung, da 90% der erstellten Objekte sie niemals erstellen müssen und Sie dennoch die richtige zustandslose API für den öffentlichen Verbrauch präsentieren müssen.
quelle
Mutable ändert die Bedeutung
const
von bitweiser Konstante in logische Konstante für die Klasse.Dies bedeutet, dass Klassen mit veränderlichen Elementen länger bitweise konstant sind und nicht mehr in schreibgeschützten Abschnitten der ausführbaren Datei angezeigt werden.
Darüber hinaus wird die Typprüfung
const
geändert , indem Elementfunktionen veränderbare Elemente ohne Verwendung ändern könnenconst_cast
.Weitere Einzelheiten finden Sie in den anderen Antworten. Ich wollte jedoch hervorheben, dass dies nicht nur der Typensicherheit dient und sich auf das kompilierte Ergebnis auswirkt.
quelle
In einigen Fällen (wie bei schlecht gestalteten Iteratoren) muss die Klasse eine Zählung oder einen anderen zufälligen Wert beibehalten, der den Haupt- "Status" der Klasse nicht wirklich beeinflusst. Dies ist am häufigsten, wo ich veränderlich verwendet sehe. Ohne veränderlich wären Sie gezwungen, die gesamte Konstanz Ihres Designs zu opfern.
Es fühlt sich für mich auch die meiste Zeit wie ein Hack an. Nützlich in sehr wenigen Situationen.
quelle
Das klassische Beispiel (wie in anderen Antworten erwähnt) und die einzige Situation, in der ich das bisher
mutable
verwendete Schlüsselwort gesehen habe , ist das Zwischenspeichern des Ergebnisses eines kompliziertenGet
Methode, bei der der Cache als Datenelement der Klasse und nicht als statische Variable in der Methode (aus Gründen der gemeinsamen Nutzung mehrerer Funktionen oder der einfachen Sauberkeit).Im Allgemeinen sind die Alternativen zur Verwendung des
mutable
Schlüsselworts normalerweise eine statische Variable in der Methode oder imconst_cast
Trick.Eine weitere ausführliche Erklärung finden Sie hier .
quelle
const_cast
nur, wenn Sie wissen (oder garantiert wurden), dass etwas nicht geändert wird (z. B. wenn Sie C-Bibliotheken stören) oder wenn Sie wissen, dass es nicht als const deklariert wurde. Das Ändern einer const-gegossenen const-Variablen führt zu undefiniertem Verhalten.const_cast
kann verwendet werden, um ein Klassenmitglied in einerconst
Methode zu ändern , worauf ich mich bezog ...const_cast
, wie gesagt, ist dies nur zulässig, wenn das Objekt nicht deklariert wurdeconst
. ZBconst Frob f; f.something();
mitvoid something() const { const_cast<int&>(m_foo) = 2;
Ergebnissen in undefiniertem Verhalten.Die veränderbare Variable kann nützlich sein, wenn Sie eine virtuelle const-Funktion überschreiben und Ihre untergeordnete Klassenmitgliedsvariable in dieser Funktion ändern möchten. In den meisten Fällen möchten Sie die Schnittstelle der Basisklasse nicht ändern, daher müssen Sie eine eigene veränderbare Mitgliedsvariable verwenden.
quelle
Das veränderbare Schlüsselwort ist sehr nützlich, wenn Sie Stubs für Klassentestzwecke erstellen. Sie können eine const-Funktion stubben und dennoch in der Lage sein, (veränderbare) Zähler oder andere Testfunktionen, die Sie Ihrem stub hinzugefügt haben, zu erhöhen. Dadurch bleibt die Schnittstelle der Stubbed-Klasse erhalten.
quelle
Eines der besten Beispiele, bei denen wir veränderlich verwenden, ist die tiefe Kopie. im Kopierkonstruktor senden wir
const &obj
als Argument. Das neu erstellte Objekt ist also vom konstanten Typ. Wenn wir die Mitglieder in diesem neu erstellten const-Objekt ändern möchten (meistens werden wir sie nicht ändern, in seltenen Fällen können wir sie ändern), müssen wir es als deklarierenmutable
.mutable
Die Speicherklasse kann nur für nicht statische nicht konstante Datenelemente einer Klasse verwendet werden. Das veränderbare Datenelement einer Klasse kann geändert werden, auch wenn es Teil eines Objekts ist, das als const deklariert ist.Im obigen Beispiel können wir den Wert der Mitgliedsvariablen ändern,
x
obwohl sie Teil eines Objekts ist, das als const deklariert ist. Dies liegt daran, dass die Variablex
als veränderbar deklariert ist. Wenn Sie jedoch versuchen, den Wert der Mitgliedsvariablen zu änderny
, gibt der Compiler einen Fehler aus.quelle
Das Schlüsselwort 'mutable' ist eigentlich ein reserviertes Schlüsselwort. Oft wird es verwendet, um den Wert der konstanten Variablen zu variieren. Wenn Sie mehrere Werte einer Konstante haben möchten, verwenden Sie das Schlüsselwort mutable.
quelle