C ++ Singleton tasarım deseni


735

Son zamanlarda C ++ için Singleton tasarım modelinin gerçekleştirilmesi / uygulanmasıyla karşılaştım. (Gerçek hayat örneğinden benimsedim) şöyle görünüyordu:

// a lot of methods are omitted here
class Singleton
{
   public:
       static Singleton* getInstance( );
       ~Singleton( );
   private:
       Singleton( );
       static Singleton* instance;
};

Bu beyandan, örnek alanının yığın üzerinde başlatıldığını çıkarabilirim. Bu, bellek tahsisi olduğu anlamına gelir. Hafızanın tam olarak yer değiştireceği zaman benim için tamamen belirsiz olan nedir? Yoksa bir hata ve bellek sızıntısı var mı? Uygulamada bir sorun var gibi görünüyor.

Asıl sorum şu, onu doğru şekilde nasıl uygularım?



10
Bu makalede, C ++ 'da iş parçacığı güvenliği ile birlikte bir singletonun nasıl uygulanacağı hakkında harika bir tartışma bulacaksınız. aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf

106
@sbi - Mutlak olarak sadece bir Sith ilgilenir. Sorunların büyük çoğunluğu Singletons olmadan çözülebilir mi? Kesinlikle. Singletons kendi sorunlarına neden oluyor mu? Evet. Ancak, dürüstçe kötü olduklarını söyleyemem , çünkü tasarım tamamen gelenekleri düşünmek ve yaklaşımınızın nüanslarını anlamakla ilgilidir.
derekerdmann

11
@derekerdmann: Asla global bir değişkene ihtiyacınız olmadığını söylemedim (ve bir taneye ihtiyacınız olduğunda bir Singleton bazen daha iyidir). Söylediğim, mümkün olduğunca az kullanılması gerektiğidir. Singleton'u değerli bir tasarım deseni olarak yüceltmek, bir hack olmaktan ziyade kodun anlaşılmasını zor, bakımını zor ve test etmeyi zorlaştırmak yerine onu kullanmanın iyi olduğu izlenimini verir . Bu yüzden yorumumu gönderdim. Söylediklerinizden hiçbiri bununla çelişmedi.
sbi

13
@sbi: Söylediğin "Onları kullanma." Çok daha makul değil "mümkün olduğunca az kullanılmalıdır" daha sonra değiştirdiniz - kesinlikle farkı görüyorsunuz.
jwd

Yanıtlar:


1106

2008 yılında tembel olarak değerlendirilmiş, garantili imhalı, teknik olarak iş parçacığı açısından güvenli olmayan Singleton tasarım modelinin bir C ++ 98 uygulamasını sağladım:
Herhangi biri bana c ++ 'da Singleton örneği sağlayabilir mi?

İşte tembel olarak değerlendirilen, doğru şekilde imha edilen ve iş parçacığı açısından güvenli olan Singleton tasarım modelinin güncellenmiş bir C ++ 11 uygulaması .

class S
{
    public:
        static S& getInstance()
        {
            static S    instance; // Guaranteed to be destroyed.
                                  // Instantiated on first use.
            return instance;
        }
    private:
        S() {}                    // Constructor? (the {} brackets) are needed here.

        // C++ 03
        // ========
        // Don't forget to declare these two. You want to make sure they
        // are unacceptable otherwise you may accidentally get copies of
        // your singleton appearing.
        S(S const&);              // Don't Implement
        void operator=(S const&); // Don't implement

        // C++ 11
        // =======
        // We can use the better technique of deleting the methods
        // we don't want.
    public:
        S(S const&)               = delete;
        void operator=(S const&)  = delete;

        // Note: Scott Meyers mentions in his Effective Modern
        //       C++ book, that deleted functions should generally
        //       be public as it results in better error messages
        //       due to the compilers behavior to check accessibility
        //       before deleted status
};

Singletonun ne zaman kullanılacağı hakkında bu makaleye bakın: (sık değil)
Singleton: Nasıl kullanılmalıdır

Başlatma sırası ve nasıl başa çıkılacağı hakkında bu iki makaleye bakın:
Statik değişkenler başlatma sırası
C ++ statik başlatma sırası sorunlarını bulma

Yaşam sürelerini açıklayan bu makaleye bakın:
C ++ işlevinde statik bir değişkenin ömrü nedir?

Tek tekler için bazı iş parçacığı sonuçlarını tartışan bu makaleye bakın:
GetInstance yönteminin statik değişkeni olarak bildirilen tekton örneği, iş parçacığı açısından güvenli mi?

Çift denetimli kilitlemenin neden C ++ üzerinde çalışmadığını açıklayan bu makaleye bakın:
Bir C ++ programcısının bilmesi gereken tüm genel tanımlanmamış davranışlar nelerdir?
Dobbs: C ++ ve Çift Kontrollü Kilitlemenin Tehlikeleri: Bölüm I


23
İyi cevap. Ancak bunun iş parçacığı açısından güvenli olmadığını unutmayın. Stackoverflow.com/questions/1661529/…
Varuna

4
@zourtney: Birçok insan ne yaptığını fark etmiyor :)
Johann Gerell

4
@MaximYegorushkin: Bu yok edildiğinde çok iyi tanımlanmıştır (belirsizlik yoktur). Bakınız: stackoverflow.com/questions/246564/…
Martin York

3
What irks me most though is the run-time check of the hidden boolean in getInstance()Bu, uygulama tekniğiyle ilgili bir varsayımdır. Hayatta olduğuna dair hiçbir varsayım gerekmez. bkz. stackoverflow.com/a/335746/14065 Bir durumu her zaman canlı olacak şekilde zorlayabilirsiniz (daha az ek yük Schwarz counter). Genel değişkenler, bir siparişi zorlamadığınız için başlatma siparişiyle (derleme birimlerinde) daha fazla sorun yaşarlar. Bu modelin avantajı 1) tembel başlatmadır. 2) Bir emri uygulama yeteneği (Schwarz yardımcı olur, ancak daha çirkindir). Evet get_instance(), çok daha çirkin.
Martin York

3
@kol: Hayır. Her zamanki gibi değil. Yeni başlayanların kodu düşünmeden kopyalayıp yapıştırmaları onu normal bir kod haline getirmez. Her zaman kullanım senaryosuna bakmalı ve atama operatörünün beklenenleri yaptığından emin olmalısınız. Kopyalama ve yapıştırma kodu sizi hatalara yönlendirecektir.
Martin York

47

Singleton olarak, genellikle yok edilmesini istemezsiniz.

Program sona erdiğinde, tek birtonun normal, istenen davranışı olan yırtılacak ve dağıtılacaktır. Açık bir şekilde temizleyebilmek istiyorsanız, sınıfa temiz bir duruma geri yüklemenizi ve bir sonraki kullanımda yeniden tahsis edilmesini sağlayan statik bir yöntem eklemek oldukça kolaydır, ancak bu, "klasik" singleton.


4
statik Statik * örneğinde silme hiçbir zaman açıkça çağrılmazsa, bu yine de teknik olarak bellek sızıntısı olarak değerlendirilmez mi?
Andrew Garrison

7
Bu artık küresel bir değişkenin basit bir bildirgesinden başka bir bellek sızıntısı değil.
ilya n.

15
Düz bir şey ayarlamak için ... "bellek sızıntısı" endişe karşı tekilleri tamamen ilgisiz. Eğer yapısöküm emrinin önemli olduğu durumsal kaynaklarınız varsa, singletonlar tehlikeli olabilir; ancak tüm bellek, program sonlandırıldığında işletim sistemi tarafından temiz bir şekilde geri kazanılır ... bu tamamen akademik noktayı vakaların% 99,9'unda geçersiz kılar. Dilbilgisini bir "bellek sızıntısı" olan ve olmayan şeyin ileri geri tartışmak istiyorsanız, bu iyi, ancak bunun gerçek tasarım kararlarından bir sapma olduğunu anlayın.
jkerian

12
@jkerian: Bellek sızıntıları ve C ++ bağlamındaki yıkım, gerçekten bellek sızıntısı ile ilgili değildir. Gerçekten kaynak kontrolü ile ilgili. Belleği sızdırırsanız, destroctor çağrılmaz ve bu nedenle nesne ile ilişkili kaynaklar doğru şekilde serbest bırakılmaz. Bellek, programlama öğretirken kullandığımız basit bir örnektir, ancak orada çok daha karmaşık kaynaklar vardır.
Martin York

7
@ Martin sana tamamen katılıyorum. Tek kaynak bellek olsa bile, "önemli olmayanları" filtreleyen bir sızıntı listesini süzmek zorunda kalırsanız, programınızda GERÇEK sızıntıları bulmaya çalışırken sorun yaşarsınız. Bunları temizlemek daha iyidir, böylece sızıntıları bildiren herhangi bir araç sadece sorun olan şeyleri rapor eder.
Dolphin

38

Bellek ayırmayı önleyebilirsiniz. Çok değişkenli ortamlarda hepsi problem olan birçok varyant vardır.

Bu tür bir uygulamayı tercih ederim (aslında, tercih ettiğim doğru şekilde söylenmez, çünkü tekillerden mümkün olduğunca kaçınırım):

class Singleton
{
private:
   Singleton();

public:
   static Singleton& instance()
   {
      static Singleton INSTANCE;
      return INSTANCE;
   }
};

Dinamik bellek tahsisi yoktur.


3
Bazı durumlarda, bu tembel başlatma, takip etmek için ideal bir model değildir. Bir örnek, singletonun kurucusunun yığından bellek ayırması ve bu tahsisin, örneğin gömülü bir sistemde veya diğer sıkı kontrol edilen bir ortamda öngörülebilir olmasını istemenizdir. Singleton kalıbı kullanılacak en iyi kalıp olduğunda, örneği sınıfın statik bir üyesi olarak oluşturmayı tercih ederim.
dma

3
Daha büyük programlar için, özellikle dinamik kütüphaneleri olanlar için. İlkel olmayan herhangi bir genel veya statik nesne, kitaplıkların boşaltılması sırasında imha sorunlarının sırası nedeniyle birçok platformda program çıkışında segfaultlara / çökmelere neden olabilir. Bu, birçok kodlama sözleşmesinin (Google'ın da dahil olduğu) önemsiz statik ve küresel nesnelerin kullanımını yasaklamasının nedenlerinden biridir.
obecalp

Bu tür bir uygulamadaki statik örneğin dahili bağlantıya sahip olduğu ve farklı çeviri biriminde kafa karıştırıcı ve yanlış davranışa neden olacak benzersiz ve bağımsız kopyalara sahip olacağı görülmektedir. Ama böyle bir uygulama gördüm, bir şey mi eksik?
FaceBro

Kullanıcının bunu sahne arkasındaki derleyicinin kendi kopya oluşturucusunu kullandığı birden çok nesneye atamasını ne engeller?
Tony Tannous

19

@Loki Astari'nin yanıtı mükemmel.

Bununla birlikte, singletonu kullanan tüm statik nesneleriniz artık gerekmedikçe singletonun imha edilmeyeceğini garanti etmeniz gereken birden fazla statik nesneye sahip zamanlar vardır .

Bu durumda , programın sonunda statik yıkıcılar çağrılsa bile singletonu tüm kullanıcılar için canlı std::shared_ptrtutmak için kullanılabilir :

class Singleton
{
public:
    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;

    static std::shared_ptr<Singleton> instance()
    {
        static std::shared_ptr<Singleton> s{new Singleton};
        return s;
    }

private:
    Singleton() {}
};

9

Tahsis etmeyen başka bir alternatif: Cihtiyacınız olduğu gibi , bir sınıf , örneğin bir tekton oluşturun :

singleton<C>()

kullanma

template <class X>
X& singleton()
{
    static X x;
    return x;
}

Ne bu ne de Cătălin'in cevabı mevcut C ++ 'da otomatik olarak iş parçacığı açısından güvenli değildir, ancak C ++ 0x'de olacaktır.


Şu anda gcc altında iplik güvenli (ve bir süredir).
Martin York

13
Bu tasarımdaki sorun, birden fazla kütüphanede kullanılması durumundadır. Her kitaplığın, kitaplığın kullandığı singletonun kendi kopyası vardır. Yani artık bir singleton değil.
Martin York

6

Cevaplar arasında bir CRTP uygulaması bulamadım, işte burada:

template<typename HeirT>
class Singleton
{
public:
    Singleton() = delete;

    Singleton(const Singleton &) = delete;

    Singleton &operator=(const Singleton &) = delete;

    static HeirT &instance()
    {
        static HeirT instance;
        return instance;
    }
};

Kullanmak için sınıfınızı bundan miras alabilirsiniz: class Test : public Singleton<Test>


1
Ben varsayılan yapıcı korumalı ve '= varsayılan;' yapılan kadar C ++ 17 ile çalışmak için alınamadı.
WFranczyk

6

Kabul edilen cevaptaki çözümün önemli bir dezavantajı vardır - kontrol main()fonksiyondan ayrıldıktan sonra singleton için yıkıcı çağrılır . İçinde bazı bağımlı nesneler tahsis edildiğinde gerçekten sorunlar olabilir main.

Qt uygulamasında bir Singleton tanıtmaya çalışırken bu sorunla karşılaştım. Tüm kurulum diyaloglarımın Singleton olması gerektiğine karar verdim ve yukarıdaki modeli kabul ettim. Ne yazık ki, Qt ana sınıfı işlevdeki QApplicationyığına tahsis edildi mainve Qt, hiçbir uygulama nesnesi olmadığında iletişim kutusu oluşturmayı / yok etmeyi yasaklıyor.

Bu yüzden yığın tahsisli singletonları tercih ederim. Tüm singletonlar için açık init()ve term()yöntemler sağlıyorum ve bunları içeri çağırıyorum main. Böylece singleton yaratma / imha sırası üzerinde tam bir kontrole sahibim ve aynı zamanda birinin çağrılıp çağrılmadığına bakılmaksızın singletonların oluşturulacağını garanti ediyorum getInstance().


2
Şu anda kabul edilen cevaba atıfta bulunuyorsanız, ilk ifade yanlıştır. Tüm statik depolama süresi nesneleri yok edilene kadar yıkıcı çağrılmaz.
Martin York

5

İşte kolay bir uygulama.

#include <Windows.h>
#include <iostream>

using namespace std;


class SingletonClass {

public:
    static SingletonClass* getInstance() {

    return (!m_instanceSingleton) ?
        m_instanceSingleton = new SingletonClass : 
        m_instanceSingleton;
    }

private:
    // private constructor and destructor
    SingletonClass() { cout << "SingletonClass instance created!\n"; }
    ~SingletonClass() {}

    // private copy constructor and assignment operator
    SingletonClass(const SingletonClass&);
    SingletonClass& operator=(const SingletonClass&);

    static SingletonClass *m_instanceSingleton;
};

SingletonClass* SingletonClass::m_instanceSingleton = nullptr;



int main(int argc, const char * argv[]) {

    SingletonClass *singleton;
    singleton = singleton->getInstance();
    cout << singleton << endl;

    // Another object gets the reference of the first object!
    SingletonClass *anotherSingleton;
    anotherSingleton = anotherSingleton->getInstance();
    cout << anotherSingleton << endl;

    Sleep(5000);

    return 0;
}

Yalnızca bir nesne oluşturuldu ve bu nesne başvurusu, her sonradan sonra döndürülüyor.

SingletonClass instance created!
00915CB8
00915CB8

Burada 00915CB8, tekil nesnenin bellek konumudur, program süresince aynıdır, ancak program her çalıştırıldığında (normalde!) Farklıdır.

Not: Bu, güvenli bir iplik değildir. İplik güvenliğini sağlamanız gerekir.


5

Nesneyi yığın halinde ayırmak istiyorsanız, neden benzersiz bir işaretçi kullanmıyorsunuz? Benzersiz bir işaretçi kullandığımız için bellek de yer değiştirir.

class S
{
    public:
        static S& getInstance()
        {
            if( m_s.get() == 0 )
            {
              m_s.reset( new S() );
            }
            return *m_s;
        }

    private:
        static std::unique_ptr<S> m_s;

        S();
        S(S const&);            // Don't Implement
        void operator=(S const&); // Don't implement
};

std::unique_ptr<S> S::m_s(0);

3
C ++ 11'de kullanımdan kaldırıldı. Bunun yerine unique_ptr önerilir. cplusplus.com/reference/memory/auto_ptr cplusplus.com/reference/memory/unique_ptr
Andrew

2
Bu güvenli değil. Daha iyi hale getirmek için m_syerel staticait getInstance()ve bir testi olmadan hemen başlatmak.
Galik

2

Gerçekten de öbekten ayrılmıştır, ancak kaynaklar olmadan bilmenin bir yolu yoktur.

Tipik uygulama (zaten emacs var bazı kod alınan) olurdu:

Singleton * Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
    };
    return instance;
};

... ve daha sonra temizlemek için kapsam dışında kalan programa güvenin.

Temizlemenin manuel olarak yapılması gereken bir platformda çalışıyorsanız, muhtemelen manuel bir temizleme rutini eklerdim.

Bu şekilde yapmanın bir başka sorunu da iş parçacığı için güvenli olmamasıdır. Çok iş parçacıklı bir ortamda, iki iş parçacığı yeni örneği ayırma şansına sahip olmadan önce "if" iletisini alabilir (her ikisi de). Yine de temizlemek için program sonlandırmaya güveniyorsanız, bu hala bir anlaşma çok büyük değil.


örnek değişkeninin sınıf örneğine bir işaretçi olduğunu görebileceğiniz için çıkarım yapabilirsiniz.
Artem Barger

3
Singletonu dinamik olarak ayırmaya gerek yoktur. Aslında, bu kötü bir fikirdir, çünkü yukarıdaki tasarımı kullanarak otomatik olarak tahsis etmenin bir yolu yoktur. Kapsam dışına çıkmasına izin vermek yıkıcı çağırmaz ve sadece tembel.
Martin York

Atexit işlevini kullanarak otomatik olarak yeniden konumlandırabilirsiniz. Yaptığımız şey bu (iyi bir fikir olduğunu söylemiyorum)
Joe

2

Kimseden bahsetti mi std::call_onceve std::once_flag? Çift kontrollü kilitleme dahil olmak üzere diğer birçok yaklaşım kırılmıştır.

Singleton kalıp uygulamasında önemli bir sorun, güvenli başlatmadır. Tek güvenli yol, başlatma sırasını senkronize bariyerlerle korumaktır. Ancak bu engellerin kendilerinin güvenli bir şekilde başlatılması gerekir. std::once_flaggüvenli başlatma garantili alma mekanizmasıdır.


2

Bu konuyu yakın zamanda EECS sınıfımda ele aldık. Ders notlarına ayrıntılı bir şekilde bakmak isterseniz, http://umich.edu/~eecs381/lecture/IdiomsDesPattsCreational.pdf adresini ziyaret edin.

Tek bir Singleton sınıfı oluşturmanın doğru olduğunu bildiğim iki yol var.

İlk Yol:

Örneğinizde olduğu gibi uygulayın. İmhaya gelince, "Tekil programlar genellikle programın çalışma süresine dayanır; çoğu işletim sistemi, bir program sona erdiğinde belleği ve diğer birçok kaynağı kurtarır, bu nedenle bu konuda endişe etmemek için bir argüman vardır."

Ancak, program sona erdiğinde temizlemek iyi bir uygulamadır. Bu nedenle, bunu bir yardımcı statik SingletonDestructor sınıfıyla yapabilir ve bunu Singleton'unuzda bir arkadaş olarak ilan edebilirsiniz.

class Singleton {
public:
  static Singleton* get_instance();

  // disable copy/move -- this is a Singleton
  Singleton(const Singleton&) = delete;
  Singleton(Singleton&&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  Singleton& operator=(Singleton&&) = delete;

  friend class Singleton_destroyer;

private:
  Singleton();  // no one else can create one
  ~Singleton(); // prevent accidental deletion

  static Singleton* ptr;
};

// auxiliary static object for destroying the memory of Singleton
class Singleton_destroyer {
public:
  ~Singleton_destroyer { delete Singleton::ptr; }
};

Singleton_destroyer program başlangıcında oluşturulur ve "program sona erdiğinde, tüm global / statik nesneler çalışma zamanı kitaplığı kapatma kodu (linker tarafından eklenir) tarafından yok edilir, böylece the_destroyer imha edilir; yıkıcı Singleton'u siler, yoketme."

İkinci Yol

Buna C ++ sihirbazı Scott Meyers tarafından oluşturulan Meyers Singleton denir. Get_instance () yöntemini farklı bir şekilde tanımlamanız yeterlidir. Artık işaretçi üye değişkeninden de kurtulabilirsiniz.

// public member function
static Singleton& Singleton::get_instance()
{
  static Singleton s;
  return s;
}

Geri dönen değer referans olduğundan ve üye değişkenlere erişmek .yerine sözdizimini kullanabildiğiniz için bu temizdir ->.

"Derleyici, bildirimde bulunarak ilk kez 's' oluşturan kodu otomatik olarak oluşturur, daha sonra değil ve sonra program sonlandırıldığında statik nesneyi siler."

Ayrıca Meyers Singleton ile "nesneler sonlandırma sırasında birbirlerine güvenirlerse çok zor duruma girebileceğinize dikkat edin - Singleton diğer nesnelere göre ne zaman yok olur? Ama basit uygulamalar için bu iyi çalışır."


1

Buradaki diğer tartışmaya ek olarak, kullanımı bir örnekle sınırlamaksızın küreselliğe sahip olabileceğinizi belirtmek gerekir. Örneğin, bir şeyi saymak için başvuru örneğini düşünün ...

struct Store{
   std::array<Something, 1024> data;
   size_t get(size_t idx){ /* ... */ }
   void incr_ref(size_t idx){ /* ... */}
   void decr_ref(size_t idx){ /* ... */}
};

template<Store* store_p>
struct ItemRef{
   size_t idx;
   auto get(){ return store_p->get(idx); };
   ItemRef() { store_p->incr_ref(idx); };
   ~ItemRef() { store_p->decr_ref(idx); };
};

Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances

Şimdi bir fonksiyonun içinde bir yerde (örneğin main) şunları yapabilirsiniz:

auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201); 

StoreReferansların, bir derleyiciyi kendi bilgilerine geri depolaması gerekmez, çünkü bu bilgi derleme zamanında sağlanır. Ayrıca Store, derleyicinin global olmasını gerektirdiği için 'nin ömrü hakkında endişelenmenize gerek yoktur . Eğer gerçekten sadece bir örneği Storevarsa, bu yaklaşımda ek yük yoktur; birden fazla örnekle kod oluşturma konusunda akıllı olmak derleyiciye kalmıştır. Gerekirse, ItemRefsınıf bile yapılabilir friendait Store(eğer şablonu var olabilir arkadaşlar!).

Eğer Storekendisi templated sınıf daha sonra işler Messier olsun, ama belki şu imzası ile bir yardımcı sınıf uygulayarak, bu yöntemi kullanmak hala mümkün:

template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning 
                       instances of ItemRef<Store_t, store_p>. */ };

Kullanıcı artık StoreWrapperher bir global Storeörnek için bir tür (ve global örnek) oluşturabilir ve her zaman mağaza örneklerine kendi sarmalayıcı örnekleri aracılığıyla erişebilir (böylece kullanmak için gerekli şablon parametrelerinin kanlı ayrıntılarını unutur Store).


0

Bu, nesne yaşam süresi yönetimi ile ilgilidir. Yazılımınızda singletondan daha fazlasının olduğunu varsayalım. Ve bunlar Logger singleton'a bağlıdır. Uygulama imhası sırasında, başka bir singleton nesnesinin imha adımlarını kaydetmek için Logger'ı kullandığını varsayın. Logger'ın en son temizlenmesi gerektiğini garanti etmelisiniz. Bu nedenle, lütfen bu makaleye de göz atın: http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf


0

Uygulamam Galik'inkine benziyor. Aradaki fark, uygulamadan çıkılan ve statik işaretçiler temizlenene kadar bellekte tutmanın aksine, paylaşılan işaretçilerin ayrılan belleği temizlemesine izin vermesidir.

#pragma once

#include <memory>

template<typename T>
class Singleton
{
private:
  static std::weak_ptr<T> _singleton;
public:
  static std::shared_ptr<T> singleton()
  {
    std::shared_ptr<T> singleton = _singleton.lock();
    if (!singleton) 
    {
      singleton.reset(new T());
      _singleton = singleton;
    }

    return singleton;
  }
};

template<typename T>
std::weak_ptr<T> Singleton<T>::_singleton;

0

Örnek işaretçisini sınıfın dışında bildirmemeniz dışında kodunuz doğrudur . Statik değişkenlerin sınıf içi bildirimleri C ++ 'da bildirimler olarak kabul edilmez, ancak buna C # veya Java gibi diğer dillerde de izin verilir .

class Singleton
{
   public:
       static Singleton* getInstance( );
   private:
       Singleton( );
       static Singleton* instance;
};
Singleton* Singleton::instance; //we need to declare outside because static variables are global

Singleton örneğinin bizim tarafımızdan manuel olarak silinmesi gerekmediğini bilmelisiniz . Tüm program boyunca tek bir nesnesine ihtiyacımız var, bu nedenle program yürütme sonunda otomatik olarak yeniden konumlandırılacak.


-1

Yukarıda bağlantılı olan kağıt, çift kontrollu kilitlemenin eksikliğini açıklar; derleyicinin nesne için belleği tahsis edebilmesi ve nesnenin yapıcısı çağrılmadan önce tahsis edilen belleğin adresine bir işaretçi ayarlayabilmesidir. Bununla birlikte, c ++ 'da belleği elle ayırmak için ayırıcılar kullanmak ve daha sonra belleği başlatmak için bir yapı çağrısı kullanmak oldukça kolaydır. Bu değerlendirmeyi kullanarak, çift kontrollu kilitleme gayet iyi çalışıyor.


2
Ne yazık ki değil. Bu, en iyi C ++ geliştiricileri tarafından derinlemesine tartışıldı. C ++ 03'te çift kontrol kilidi kırılmıştır.
Martin York

-1
#define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;}

Misal:

   class CCtrl
    {
    private:
        CCtrl(void);
        virtual ~CCtrl(void);

    public:
        INS(CCtrl);

-1

Basit singleton sınıfı, Bu başlık sınıf dosyanız olmalıdır

#ifndef SC_SINGLETON_CLASS_H
#define SC_SINGLETON_CLASS_H

class SingletonClass
{
    public:
        static SingletonClass* Instance()
        {
           static SingletonClass* instance = new SingletonClass();
           return instance;
        }

        void Relocate(int X, int Y, int Z);

    private:
        SingletonClass();
        ~SingletonClass();
};

#define sSingletonClass SingletonClass::Instance()

#endif

Singletonunuza şu şekilde erişin:

sSingletonClass->Relocate(1, 2, 5);

-3

Statik nesnenizin silindiği statik bir işlev yazmanız gerektiğini düşünüyorum. Uygulamanızı kapatmak üzereyken bu işlevi çağırmalısınız. Bu, bellek sızıntısı olmamasını sağlayacaktır.

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.