'Mutable' anahtar sözcüğünün değişkenin bir const işlevi tarafından değiştirilmesine izin vermekten başka bir amacı var mı?


527

Bir süre önce bir sınıfın üye değişkenini mutableanahtar kelimeyle işaretleyen bazı kodlarla karşılaştım . Görebildiğim kadarıyla basitçe bir constyöntemde bir değişkeni değiştirmek için izin verir :

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

Bu, bu anahtar kelimenin tek kullanımı mı yoksa göze çarpandan daha fazlası mı var? O zamandan beri bu tekniği bir sınıfta kullandım, işlevlerin iplik güvenliği nedeniyle kilitlenmesine boost::mutexizin veren bir değişebilir olarak işaretledim const, ama dürüst olmak gerekirse, bir kesmek gibi geliyor.


2
Yine de bir soru, eğer hiçbir şeyi değiştirmiyorsanız, neden ilk etapta bir muteksi kullanmanız gerekiyor? Sadece bunu anlamak istiyorum.
Yanlış Evlenme

@Misgevolution bir şeyi değiştiriyorsunuz, sadece const yoluyla değişikliği kimin / nasıl yapabileceğini kontrol ediyorsunuz. Gerçekten naif bir örnek, sadece arkadaşlara sabit olmayan kulplar verirsem, düşmanların sabit bir ele sahip olduğunu hayal edin. Arkadaşlar değiştirebilir, düşman değiştiremez.
iheanyi

1
Not: İşte anahtar kelime kullanımı için harika bir örnek mutable: stackoverflow.com/questions/15999123/…
Gabriel Staples

Ben bunu constyapmak zorunda değilsiniz (tür) geçersiz kılmak için kullanılabilmesini diliyorum : class A_mutable{}; using A = A_mutable const; mutable_t<A> a;Eğer const-tarafından-varsayılan, yani mutable A a;(açık mutable) ve A a;(örtülü const) istiyorum.
alfC

Yanıtlar:


351

Bitsel sabit ve mantıksal sabitin farklılaşmasına izin verir. Mantıksal sabit, bir nesnenin, kilitleme örneğiniz gibi, genel arabirimden görülebilecek şekilde değişmediği zamandır. Başka bir örnek, ilk istendiğinde bir değeri hesaplayan ve sonucu önbelleğe alan bir sınıftır.

C ++ 11 mutable, lambda'da değere göre yakalanan şeylerin değiştirilebilir olduğunu belirtmek için kullanılabildiğinden (varsayılan olarak değildir):

int x = 0;
auto f1 = [=]() mutable {x = 42;};  // OK
auto f2 = [=]()         {x = 42;};  // Error: a by-value capture cannot be modified in a non-mutable lambda

52
'mutable' bitsel / mantıksal sabitliği hiç etkilemez. C ++ yalnızca bitsel olarak sabittir ve 'mutable' anahtar sözcüğü, üyeleri bu denetlemenin dışında bırakmak için kullanılabilir. C ++ 'da soyutlamalar (örn. SmartPtrs) dışında' mantıksal 'sabit elde etmek mümkün değildir.
Richard Corden

111
@Richard: noktayı kaçırıyorsun. "Mantıksal const" anahtar sözcüğü yoktur, doğrudur, bunun yerine, programcının nesnenin mantıklı gözlemlenebilir durumunu neyin oluşturduğuna dair bir anlayışa dayanarak, hangi üyelerin değişebilir hale getirilerek dışlanması gerektiğine karar vermek için yaptığı kavramsal bir farklılaşmadır.
Tony Delroy

6
@ajay Evet, bu bir değişkeni değişebilir olarak sabit nesnelerin içinde değiştirilmesine izin vermek için bütün nokta.
KeithB

6
Neden lambdalarda değişime ihtiyaç var? Değişkeni referans olarak yakalamak yeterli olmaz mı?
Giorgio

11
@Giorgio: Fark, xlambda içindeki modifiye edilmiş lambda içinde kalması, yani lambda fonksiyonunun sadece kendi kopyasını değiştirebilmesidir x. Değişiklik dışarıda görünmez, orijinal xhala değişmez. Lambdaların functor sınıfları olarak uygulandığını düşünün; yakalanan değişkenler üye değişkenlere karşılık gelir.
Sebastian Mach

138

mutableAnahtar kelime delmek için bir yoldur constsize nesneleri üzerinde yayılacak peçe. Bir nesneye const referansınız veya işaretçiniz varsa, o nesneyi ne zaman ve nasıl işaretlendiği dışında hiçbir şekilde değiştiremezsiniz mutable.

Senin ile constbaşvuru veya pointer size kısıtlanır:

  • yalnızca görünür veri üyeleri için okuma erişimi
  • yalnızca olarak işaretlenmiş yöntemleri çağırma izni const.

mutableŞimdi yazabilir veya işaretlenmiş ayarlanan veri üyeleri böylece istisna yapar mutable. Dışarıdan görünen tek fark bu.

Dahili constolarak size göre görünen yöntemler işaretli veri üyelerine de yazabilir mutable. Esasen, yapı perdesi kapsamlı bir şekilde delinmiştir. Konsepti mutableyok etmediğinden constve yalnızca yararlı özel durumlarda kullanıldığından emin olmak tamamen API tasarımcısına bağlıdır . mutableAnahtar kelime bu özel durumlar tabi bunun nedeni açıkça işaretleri veri üye olur.

Uygulamada constkod tabanınız boyunca obsesif olarak kullanabilirsiniz (esasen kod tabanınızı const"hastalık" ile "enfekte etmek istersiniz ). Bu dünyada işaretçiler ve referanslar constçok az istisna dışında, akıl yürütmesi ve anlaması daha kolay olan kod verir. İlginç bir araştırma için "referans şeffaflığı" konusuna bakın.

mutableAnahtar kelime olmadan, sonunda const_castçeşitli yararlı özel durumları (önbellekleme, ref sayımı, hata ayıklama verileri, vb.) Ne yazık ki API istemcisini kullandığı nesnelerin / nesnelerin korunmasını yok etmeye zorladığı için const_castönemli ölçüde daha yıkıcıdır . Ek olarak yaygın yıkıma neden olur : bir sabit işaretçi veya başvuru oluşturmak, görünür üyelere serbest yazma ve yöntem çağırma erişimi sağlar. Bunun aksine , API tasarımcısının istisnalar üzerinde hassas bir kontrol gerçekleştirmesini gerektirir ve genellikle bu istisnalar özel veriler üzerinde çalışan yöntemlerde gizlenir .mutableconstconstconst_castmutableconstconst

(Not: Birkaç kez veri ve yöntem görünürlüğüne atıfta bulunuyorum . Burada tartışılan tamamen farklı bir nesne koruma türü olan, kamuya açık veya özel veya korumalı olarak işaretlenmiş üyelerden bahsediyorum .)


8
Ayrıca, const_castbir constnesnenin bir parçasını değiştirmek için kullanılması tanımsız davranış sağlar.
Brian

Kabul etmiyorum çünkü API istemcisini nesnelerin const korumasını yok etmeye zorluyor . Kullandığınız takdirde const_castbir üye değişkenlerin mutasyonu uygulamak için constyöntem cast yapmak için istemci sormak olmaz - Bunu yapardım yöntemi içinde tarafından const_casting this. Temel olarak, belirli bir çağrı sitesindeki keyfi üyelerin sabitliğini atlamanıza mutableizin verirken , tüm çağrı sitelerinde belirli bir üyenin sabitini kaldıralım . İkincisi genellikle tipik kullanım (önbellekleme, istatistikler) için istediğiniz şeydir, ancak bazen const_cast kalıba uyar.
BeeOnRope

1
const_castDesen daha iyi böyle geçici bir üyeyi değiştirmek istediğinizde gibi bazı durumlarda, formlarını yapar, o zaman (hemen hemen gibi geri boost::mutex). Son durum ilkiyle aynı olduğu için yöntem mantıklı bir şekilde sabittir, ancak bu geçici değişikliği yapmak istersiniz. const_castorada yararlı olabilir, çünkü mutasyon geri alınacaksa özellikle bu yöntemde const atmanıza izin verir, ancak tüm yöntemlerden mutableconst korumasını kaldıracağından uygun olmaz çünkü , geri al "desen.
BeeOnRope

2
Const tanımlı nesnenin salt okunur belleğe (daha genel olarak, salt okunur olarak işaretlenmiş bellek ) olası yerleşimi ve buna izin veren ilgili standart dil, const_castbunun olası bir zaman bombasını oluşturmasını sağlar. mutablebu tür nesneler salt okunur belleğe yerleştirilemediğinden böyle bir sorun yoktur.
BeeOnRope

75

Boost :: mutex ile kullanımınız, tam olarak bu anahtar kelimenin amacıdır. Başka bir kullanım hızı hızlı erişim için dahili sonuç önbellekleme içindir.

Temel olarak 'mutable', nesnenin harici olarak görünür durumunu etkilemeyen herhangi bir sınıf niteliği için geçerlidir.

Sorunuzdaki örnek kodda, done_ değeri harici durumu etkiliyorsa, mutable uygun olmayabilir, ... Bölüm.


35

Değişken, belirli özniteliğin constyöntemlerin içinden değiştirilebilir olarak işaretlenmesidir . Tek amacı bu. Kullanmadan önce dikkatlice düşünün, çünkü tasarımı kullanmak yerine tasarımı değiştirirseniz kodunuz muhtemelen daha temiz ve daha okunabilir olacaktır mutable.

http://www.highprogrammer.com/alan/rants/mutable.html

Eğer yukarıdaki delilik değişebilir ne için değilse, ne için? İşte ince durum: mutable, bir nesnenin mantıksal olarak sabit olduğu, ancak pratikte değişmesi gereken durum içindir. Bu vakalar çok az ve çok uzak, ama var.

Yazarın verdiği örnekler, önbellekleme ve geçici hata ayıklama değişkenlerini içerir.


2
Sanırım bu bağlantı, mutable'ın yararlı olduğu bir senaryonun en iyi örneğini veriyor. (doğru kullanım başına)
enthusiasticgeek

Kullanımı mutablekodu daha okunaklı ve temiz hale getirebilir. Aşağıdaki örnekte, beklendiği gibi readolabilir const. mutable m_mutex; Kapsayıcı m_container; void add (Öğe öğesi) {Lockguard lock (m_mutex); (madde) m_container.pushback; } Öğe read () const {Kilit kilidi (m_mutex); return m_container.first (); } `
Th. Thielemann

Son derece popüler bir kullanım durumu var: ref sayımları.
Seva Alekseyev

33

Önbellek gibi dahili durumunuzu gizlediğiniz durumlarda kullanışlıdır. Örneğin:

Sınıf HashTable
{
...
halka açık:
    dize arama (dize anahtarı) const
    {
        eğer (anahtar == lastKey)
            lastValue değerini döndür;

        string value = lookupInternal (anahtar);

        lastKey = anahtar;
        lastValue = değer;

        geri dönüş değeri;
    }

özel:
    değişken dize lastKey, lastValue;
};

Ve sonra bir const HashTablenesnenin hala lookup()dahili önbelleğini değiştiren yöntemini kullanmasını sağlayabilirsiniz .


9

mutable biri, aksi takdirde sabit bir işlevde veri değiştirmek için izin vermek çıkarım var.

Amaç, nesnenin iç durumuna "hiçbir şey yapmayan" bir işleve sahip olmanız ve bu nedenle işlevi işaretlemenizdir const, ancak bazı nesne durumlarını, doğru durumunu etkilemeyecek şekilde değiştirmeniz gerekebilir. işlevsellik.

Anahtar kelime derleyiciye bir ipucu görevi görebilir - teorik bir derleyici, salt okunur olarak işaretlenmiş sabit bir nesneyi (global gibi) yerleştirebilir. Bunun mutableyapılmaması gereken ipuçlarının varlığı .

Değişken verileri bildirmek ve kullanmak için geçerli bazı nedenler:

  • İplik güvenliği. A beyanı mutable boost::mutextamamen makul.
  • İstatistik. Bağımsız değişkenlerinin bir kısmı veya tamamı göz önüne alındığında, bir işleve çağrı sayısını sayma.
  • Memoization. Bazı pahalı cevapları hesaplayın ve daha sonra yeniden hesaplamak yerine ileride başvurmak üzere saklayın.

2
İyi yanıt, mutable bir "ipucu" olma ile ilgili yorum hariç. Bu, derleyici nesneyi ROM'a yerleştirdiyse, değişebilir üye bazen değiştirilebilir olmayacak gibi görünür. Değişken davranışı iyi tanımlanmıştır.
Richard Corden

2
Bir const nesnesini salt okunur belleğe yerleştirmenin yanı sıra, örneğin, const fucntion çağrılarını bir döngüden optimize etmeye de karar verebilir. Aksi const işlevindeki değiştirilebilir bir istatistik sayacı, yalnızca daha fazla çağrı saymak amacıyla optimizasyonu önlemek yerine bu tür bir optimizasyona (ve yalnızca bir çağrıya) izin verir.
Hagen von Eitzen

@HagenvonEitzen - Bunun yanlış olduğundan eminim. Derleyici, yan etki olmadığını ispatlayamadıkça işlevleri bir döngüden kaldıramaz. Bu kanıt, genel olarak aslında fonksiyonu (o satır içi genellikle sonra) ve güvenerek değil uygulanmasını teftiş içerir const(ve böyle bir denetim başarılı veya bağımsız olarak başarısız olur constveya mutable). Yalnızca işlevi bildirmek constyeterli değildir: bir constişlev, global bir değişkeni veya işleve iletilen bir şeyi değiştirmek gibi yan etkilere sahip olmakta özgürdür, bu nedenle bu kanıt için yararlı bir garanti değildir.
BeeOnRope

Şimdi, bazı derleyicilerin gcc'nin _attribute __ ((const)) ve __attribute __ ((pure)) gibi özel uzantıları vardır , bu _do'nun bu tür etkileri vardır , ancak bu sadece constC ++ 'daki anahtar kelimeyle teğetsel olarak ilişkilidir .
BeeOnRope

8

Evet, öyle yapar. Bir sınıfın durumunu mantıksal olarak değiştirmeyen yöntemlerle değiştirilen üyeler için kullanıyorum - örneğin, önbellek uygulayarak aramaları hızlandırmak için:

class CIniWrapper
{
public:
   CIniWrapper(LPCTSTR szIniFile);

   // non-const: logically modifies the state of the object
   void SetValue(LPCTSTR szName, LPCTSTR szValue);

   // const: does not logically change the object
   LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;

   // ...

private:
   // cache, avoids going to disk when a named value is retrieved multiple times
   // does not logically change the public interface, so declared mutable
   // so that it can be used by the const GetValue() method
   mutable std::map<string, string> m_mapNameToValue;
};

Şimdi, bunu dikkatli kullanmalısınız - bir arayan sadece constyöntemler kullanıyorsa iş parçacığı güvenli olduklarını varsayabileceği için eşzamanlılık sorunları büyük bir endişe kaynağıdır . Ve elbette, mutableverilerin değiştirilmesi, nesnenin davranışını önemli bir şekilde değiştirmemelidir, örneğin diske yazılan değişikliklerin uygulama tarafından hemen görünmesi bekleniyorsa, verdiğim örnekle ihlal edilebilecek bir şey .


6

Mutable, sınıf içinde yalnızca bir sınıf veya mutex veya kilit gibi şeyleri bildirmek için kullanılan bir değişkeniniz olduğunda kullanılır. Bu değişken, sınıfın davranışını değiştirmez, ancak sınıfın kendisinin iş parçacığı güvenliğini uygulamak için gereklidir. "Değişken" olmadan, bu nedenle "cons" fonksiyonlarına sahip olamazsınız çünkü bu değişkenin dış dünyadaki tüm fonksiyonlarda değiştirilmesi gerekecektir. Bu nedenle, bir üye değişkeni bir const fonksiyonu tarafından bile yazılabilir kılmak için mutable eklenmiştir.

Belirtilen değişken hem derleyiciye hem de okuyucuya bir üye değişkenin bir sabit üye işlevi içinde değiştirilebileceğinin güvenli olduğunu ve beklendiğini bildirir.


4

mutable esas olarak sınıfın bir uygulama detayında kullanılır. Sınıfın kullanıcısı bunu bilmek zorunda değildir, bu nedenle yöntem "cons" olabilir gerektiğini düşünüyor. Muteksin değişebilir olması örneği iyi bir kanonik örnektir.


4

Bunu kullanmanız bir hack değildir, ancak C ++ 'da birçok şey gibi mutable , geri dönüp const olmayan bir şey olarak işaretlenmemesi gereken bir şeyi işaretlemek istemeyen tembel bir programcı için hacklenebilir.


3

Kullanıcıya MANTIKSIZ vatansız olan (ve bu nedenle genel sınıf API'larında "const" alıcıları olmalıdır) ancak temel UYGULAMADA (.cpp kodunuzda kod) durumsuz OLMAYAN şeyler için "mutable" kullanın.

En sık kullandığım durumlar, devletsiz "sade eski veri" üyelerinin tembel başlatılmasıdır. Yani, bu tür üyelerin inşa etmek (işlemek) veya taşımak (bellek) için pahalı olduğu ve nesnenin birçok kullanıcısının asla istemeyeceği dar durumlarda idealdir. Bu durumda, performans için arka uçta tembel inşaat yapmak istersiniz, çünkü inşa edilen nesnelerin% 90'ının onları asla inşa etmesi gerekmeyecektir, ancak yine de kamu tüketimi için doğru vatansız API'yi sunmanız gerekir.


2

Mutable const, bitsel sabitin anlamını sınıf için mantıksal sabit olarak değiştirir.

Bu, değiştirilebilir üyelere sahip sınıfların artık bitsel olarak sabit oldukları ve yürütülebilir dosyanın salt okunur bölümlerinde görünmeyecekleri anlamına gelir.

Ayrıca, constüye işlevlerinin değiştirilebilir üyeleri kullanmadan değiştirmesine izin vererek tür denetimini değiştirir const_cast.

class Logical {
    mutable int var;

public:
    Logical(): var(0) {}
    void set(int x) const { var = x; }
};

class Bitwise {
    int var;

public:
    Bitwise(): var(0) {}
    void set(int x) const {
        const_cast<Bitwise*>(this)->var = x;
    }
};

const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.

int main(void)
{
    logical.set(5); // Well defined.
    bitwise.set(5); // Undefined.
}

Daha fazla ayrıntı için diğer cevaplara bakın, ancak bunun sadece tür güvenliği için olmadığını ve derlenmiş sonucu etkilediğini vurgulamak istedim.


1

Bazı durumlarda (kötü tasarlanmış yineleyiciler gibi), sınıfın bir sayıyı veya başka bir tesadüfi değeri tutması gerekir, bu da sınıfın ana "durumunu" gerçekten etkilemez. Bu çoğunlukla mutable kullanıldığını gördüğüm yerdir. Değişken olmadan, tasarımınızın tüm yapısını feda etmek zorunda kalacaksınız.

Bana da çoğu zaman bir saldırı gibi geliyor. Çok az durumda kullanışlıdır.


1

Klasik örnek (diğer cevaplarda belirtildiği gibi) ve mutableşimdiye kadar kullanılan anahtar kelimeyi gördüğüm tek durum Get, önbelleğin sınıfın bir veri üyesi olarak değil, sınıfın bir veri üyesi olarak uygulandığı karmaşık bir yöntemin sonucunu önbelleğe almak içindir. yöntemdeki statik değişken (çeşitli işlevler veya düz temizlik arasında paylaşım nedeniyle).

Genel olarak, mutableanahtar kelimeyi kullanmanın alternatifleri genellikle yöntemde veya const_castnumarada statik bir değişkendir .

Başka bir ayrıntılı açıklama burada .


1
Değişken üyelere genel bir alternatif olarak statik üyeleri kullandığımı hiç duymadım. Ve const_castsadece bir şeyin değişmeyeceğini bildiğinizde (veya garantili olduğunuzda) (örneğin C kütüphanelerine müdahale ederken) veya bunun const olarak bildirilmediğini bildiğiniz zaman içindir. Yani, sabitlenmiş bir const değişkeninin değiştirilmesi tanımlanmamış davranışla sonuçlanır.
Sebastian Mach

1
@phresnel "Statik değişkenler" ile yöntemde (çağrılar arasında kalan) statik otomatik değişkenleri kastediyorum. Ve const_castconst
anlattığım

1
"Genel olarak" yazdığınız gibi, bu benim için açık değildi :) Değiştirmekle ilgili const_castolarak, söylendiği gibi, buna sadece nesne bildirilmediğinde izin verilir const. Örneğin const Frob f; f.something();, void something() const { const_cast<int&>(m_foo) = 2;tanımlanmamış davranış ile sonuçlanır.
Sebastian Mach

1

Değişken bir const sanal işlevini geçersiz kıldığınızda ve bu işlevdeki alt sınıf üyesi değişkeninizi değiştirmek istediğinizde kullanışlı olabilir. Çoğu durumda, temel sınıfın arabirimini değiştirmek istemezsiniz, bu nedenle kendi değişken değişkeninizi kullanmanız gerekir.


1

Mutable anahtar sözcüğü, sınıf testi amacıyla taslaklar oluştururken çok kullanışlıdır. Bir const işlevini saplayabilir ve sayaçları veya saplamanıza eklediğiniz herhangi bir test işlevselliğini yine de artırabilir (değiştirilebilir). Bu, stubbed sınıfının arayüzünü olduğu gibi tutar.


0

Değişken kullandığımız en iyi örneklerden biri derin kopyadır. kopya yapıcısında const &objargüman olarak göndeririz . Böylece oluşturulan yeni nesne sabit tipte olacaktır. Değiştirmek istiyorsak (çoğunlukla değişmeyiz, nadiren değişebiliriz) bu yeni oluşturulan const nesnesindeki üyeleri olarak ilan etmeliyiz mutable.

mutabledepolama sınıfı yalnızca bir sınıfın statik olmayan sabit veri üyesi üzerinde kullanılabilir. Bir sınıfın değişken veri üyesi, const olarak bildirilen bir nesnenin parçası olsa bile değiştirilebilir.

class Test
{
public:
    Test(): x(1), y(1) {};
    mutable int x;
    int y;
};

int main()
{
    const Test object;
    object.x = 123;
    //object.y = 123;
    /* 
    * The above line if uncommented, will create compilation error.
    */   

    cout<< "X:"<< object.x << ", Y:" << object.y;
    return 0;
}

Output:-
X:123, Y:1

Yukarıdaki örnekte, xconst olarak bildirilen bir nesnenin parçası olmasına rağmen üye değişkenin değerini değiştirebiliriz . Bunun nedeni değişkenin değişken xolarak bildirilmesidir. Ancak üye değişkenin değerini değiştirmeye çalışırsanız y, derleyici hata verir.


-1

'Mutable' anahtar kelimesi aslında ayrılmış bir anahtar kelimedir. Genellikle sabit değişkenin değerini değiştirmek için kullanılır.

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.