C ++ Singleton design pattern

736

Baru-baru ini saya bertemu dengan realisasi / implementasi pola desain Singleton untuk C ++. Itu terlihat seperti ini (saya telah mengadopsi dari contoh kehidupan nyata):

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

Dari deklarasi ini saya dapat menyimpulkan bahwa bidang instance diinisiasi di heap. Itu berarti ada alokasi memori. Apa yang benar-benar tidak jelas bagi saya adalah kapan tepatnya memori itu akan dialokasikan? Atau ada kebocoran bug dan memori? Sepertinya ada masalah dalam implementasinya.

Pertanyaan utama saya adalah, bagaimana cara menerapkannya dengan cara yang benar?

Artem Barger
sumber
10
Anda akan menemukan diskusi yang bagus tentang bagaimana menerapkan singleton, bersama dengan keamanan thread dalam C ++ dalam makalah ini. aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf
106
@sbi - Hanya transaksi Sith dalam absolut. Bisakah sebagian besar masalah diselesaikan tanpa Lajang? Benar. Apakah Lajang menyebabkan masalah sendiri? Iya. Namun, saya tidak dapat dengan jujur ​​mengatakan bahwa itu buruk , karena desain adalah tentang mempertimbangkan pengorbanan dan memahami nuansa pendekatan Anda.
derekerdmann
11
@derekerdmann: Saya tidak mengatakan Anda tidak perlu variabel global (dan ketika Anda membutuhkannya, Singleton terkadang lebih baik). Apa yang saya katakan adalah bahwa mereka harus digunakan sesedikit mungkin. Memuliakan Singleton sebagai pola desain yang berharga memberi kesan itu baik untuk digunakan, alih-alih itu adalah peretasan , membuat kode sulit dipahami, sulit dirawat, dan sulit diuji. Inilah sebabnya saya memposting komentar saya. Tak satu pun dari apa yang Anda katakan sejauh ini bertentangan dengan ini.
sbi
13
@ sbi: Apa yang Anda katakan adalah "Jangan gunakan mereka." Bukan yang jauh lebih masuk akal "mereka harus digunakan sesedikit mungkin" Anda kemudian berubah menjadi - pasti Anda melihat perbedaannya.
jwd

Jawaban:

1106

Pada tahun 2008 saya memberikan implementasi C ++ 98 dari pola desain Singleton yang dievaluasi malas, dijamin kehancuran, tidak aman secara teknis:
Bisakah ada yang memberi saya sampel Singleton di c ++?

Berikut ini adalah implementasi C ++ 11 yang diperbarui dari pola desain Singleton yang dievaluasi dengan malas, dihancurkan dengan benar, dan aman dari benang .

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
};

Lihat artikel ini tentang kapan menggunakan singleton: (tidak sering)
Singleton: Bagaimana seharusnya digunakan

Lihat dua artikel ini tentang urutan inisialisasi dan cara mengatasinya:
Urutan inisialisasi variabel statis
Menemukan masalah C + + inisialisasi statis

Lihat artikel ini yang menggambarkan masa hidup:
Apa masa pakai variabel statis dalam fungsi C ++?

Lihat artikel ini yang membahas beberapa implikasi threading untuk lajang:
Contoh Singleton dideklarasikan sebagai variabel statis metode GetInstance, apakah ini aman untuk thread?

Lihat artikel ini yang menjelaskan mengapa penguncian ganda tidak akan bekerja pada C ++:
Apa saja perilaku tak terdefinisi umum yang harus diketahui oleh seorang programmer C ++?
Dr Dobbs: C ++ dan The Perils of Double-Checked Locking: Bagian I

Martin York
sumber
23
Jawaban yang bagus. Tetapi harus dicatat bahwa ini bukan thread-safe stackoverflow.com/questions/1661529/…
Varuna
4
@ zourtney: Banyak orang tidak menyadari apa yang baru saja Anda lakukan :)
Johann Gerell
4
@ MaximYegorushkin: Ketika ini dihancurkan didefinisikan dengan sangat baik (tidak ada ambiguitas). Lihat: stackoverflow.com/questions/246564/…
Martin York
3
What irks me most though is the run-time check of the hidden boolean in getInstance()Itu adalah asumsi pada teknik implementasi. Tidak perlu ada asumsi bahwa itu hidup. lihat stackoverflow.com/a/335746/14065 Anda dapat memaksa suatu situasi agar selalu hidup (lebih sedikit overhead daripada Schwarz counter). Variabel global memiliki lebih banyak masalah dengan pesanan inisialisasi (lintas unit kompilasi) karena Anda tidak memaksakan pesanan. Kelebihan dari model ini adalah 1) inisialisasi malas. 2) Kemampuan untuk menegakkan perintah (Schwarz membantu tetapi lebih buruk). Yap get_instance()jauh lebih jelek.
Martin York
3
@ Kol: Tidak. Bukan yang biasa. Hanya karena pemula menyalin dan menempelkan kode tanpa berpikir tidak menjadikannya biasa. Anda harus selalu melihat use case dan memastikan operator penugasan melakukan apa yang diharapkan. Menyalin dan menempelkan kode akan membawa Anda ke kesalahan.
Martin York
47

Menjadi seorang Singleton, Anda biasanya tidak ingin itu dihancurkan.

Ini akan dirobohkan dan didelokasi ketika program berakhir, yang merupakan perilaku normal yang diinginkan untuk seorang lajang. Jika Anda ingin dapat membersihkannya secara eksplisit, cukup mudah untuk menambahkan metode statis ke kelas yang memungkinkan Anda untuk mengembalikannya ke kondisi bersih, dan mengalokasikannya kembali saat digunakan, tetapi itu di luar ruang lingkup suatu singleton "klasik".

Reed Copsey
sumber
4
jika delete tidak pernah secara eksplisit dipanggil pada instance Singleton * statis, bukankah ini masih dianggap kebocoran memori secara teknis?
Andrew Garrison
7
Ini bukan kebocoran memori lagi dari deklarasi sederhana dari variabel global.
ilya n.
15
Untuk meluruskan sesuatu ... "kebocoran memori" menyangkut vis-a-vis singletons benar-benar tidak dapat dibatalkan. Jika Anda memiliki sumber daya negara di mana perintah dekonstruksi penting, lajang bisa berbahaya; tetapi semua memori dengan bersih diperoleh kembali oleh sistem operasi pada penghentian program ... membatalkan titik akademis total ini dalam 99,9% kasus. Jika Anda ingin memperdebatkan tata bahasa bolak-balik tentang apa yang ada dan bukan "kebocoran memori", itu bagus, tetapi sadari bahwa itu adalah gangguan dari keputusan desain yang sebenarnya.
jkerian
12
@kerkerian: Kebocoran dan kehancuran memori dalam konteks C ++ sebenarnya bukan tentang kebocoran memori. Sungguh ini tentang kontrol sumber daya. Jika Anda membocorkan memori, Destroctor tidak dipanggil dan dengan demikian semua sumber daya yang terkait dengan objek tidak dirilis dengan benar. Memori hanyalah contoh sederhana yang kita gunakan saat mengajar pemrograman tetapi ada banyak sumber daya yang lebih kompleks di luar sana.
Martin York
7
@ Martin Saya setuju dengan Anda sepenuhnya. Bahkan jika satu-satunya sumber daya adalah memori, Anda masih akan mendapat kesulitan mencoba menemukan kebocoran NYATA dalam program Anda jika Anda harus memeriksa daftar kebocoran, menyaring yang "tidak masalah." Lebih baik untuk membersihkan semua ini sehingga alat apa pun yang melaporkan kebocoran hanya melaporkan hal-hal yang ADALAH masalah.
Dolphin
38

Anda dapat menghindari alokasi memori. Ada banyak varian, semuanya memiliki masalah dalam hal lingkungan multithreading.

Saya lebih suka implementasi semacam ini (sebenarnya, tidak benar mengatakan saya lebih suka, karena saya menghindari lajang sebanyak mungkin):

class Singleton
{
private:
   Singleton();

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

Tidak memiliki alokasi memori dinamis.

Cătălin Pitiș
sumber
3
Dalam beberapa kasus, inisialisasi malas ini bukan pola yang ideal untuk diikuti. Salah satu contoh adalah jika konstruktor dari singleton mengalokasikan memori dari heap dan Anda ingin alokasi itu dapat diprediksi, misalnya dalam sistem tertanam atau lingkungan yang dikontrol ketat lainnya. Saya lebih suka, ketika pola Singleton adalah pola terbaik untuk digunakan, untuk membuat contoh sebagai anggota statis kelas.
dma
3
Untuk banyak program yang lebih besar, terutama yang memiliki perpustakaan dinamis. Objek global atau statis apa pun yang tidak primitif dapat menyebabkan segfault / crash saat keluar dari program pada banyak platform karena urutan masalah kehancuran pada perpustakaan yang dibongkar. Ini adalah salah satu alasan banyak konvensi pengkodean (termasuk Google) melarang penggunaan objek statis dan global yang tidak sepele.
obecalp
Tampaknya contoh statis dalam implementasi tersebut memiliki tautan internal, dan akan memiliki salinan yang unik dan independen di unit terjemahan yang berbeda, yang akan menyebabkan perilaku yang membingungkan dan salah. Tetapi saya melihat banyak implementasi seperti itu, apakah saya melewatkan sesuatu?
FaceBro
Apa yang mencegah pengguna dari menetapkan ini ke beberapa objek di mana kompiler di belakang layar menggunakan konstruktor salin sendiri?
Tony Tannous
19

Jawaban @Loki Astari luar biasa.

Namun ada kalanya dengan beberapa objek statis di mana Anda harus dapat menjamin bahwa singleton tidak akan dihancurkan sampai semua objek statis Anda yang menggunakan singleton tidak lagi membutuhkannya.

Dalam hal ini std::shared_ptrdapat digunakan untuk menjaga singleton tetap hidup untuk semua pengguna bahkan ketika destruktor statis dipanggil di akhir program:

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() {}
};
Galik
sumber
9

Alternatif non-alokasi lain: buat singleton, katakanlah kelas C, sesuai kebutuhan Anda:

singleton<C>()

menggunakan

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

Baik ini maupun jawaban Cătălin secara otomatis aman di C ++ saat ini, tetapi akan berada di C ++ 0x.

James Hopkin
sumber
Saat ini di bawah gcc itu adalah thread aman (dan sudah cukup lama).
Martin York
13
Masalah dengan desain ini adalah jika digunakan di banyak pustaka. Setiap perpustakaan memiliki salinan singleton yang digunakan perpustakaan itu sendiri. Jadi bukan lagi singleton.
Martin York
6

Saya tidak menemukan implementasi CRTP di antara jawaban, jadi ini dia:

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;
    }
};

Untuk menggunakan cukup mewarisi kelas Anda dari ini, seperti: class Test : public Singleton<Test>

Yuriy
sumber
1
Tidak dapat menjalankan ini dengan C ++ 17 hingga saya membuat konstruktor default terlindungi dan '= default;'.
WFranczyk
6

Solusi dalam jawaban yang diterima memiliki kelemahan yang signifikan - destruktor untuk singleton dipanggil setelah kontrol meninggalkan main()fungsi. Mungkin ada masalah sebenarnya, ketika beberapa objek dependen dialokasikan di dalamnyamain .

Saya menemui masalah ini, ketika mencoba memperkenalkan Singleton di aplikasi Qt. Saya memutuskan, bahwa semua dialog pengaturan saya harus berupa Singletons, dan mengadopsi pola di atas. Sayangnya, kelas utama Qt QApplicationdialokasikan pada stack di Internetmain fungsi, dan Qt melarang membuat / menghancurkan dialog ketika tidak ada objek aplikasi yang tersedia.

Itulah sebabnya saya lebih suka lajang yang dialokasikan untuk tumpukan. Saya memberikan metode init()dan eksplisit term()untuk semua lajang dan memanggil mereka di dalam main. Jadi saya memiliki kontrol penuh atas urutan penciptaan / penghancuran lajang, dan saya juga menjamin bahwa lajang akan dibuat, tidak peduli apakah seseorang memanggil getInstance()atau tidak.

SadSido
sumber
2
Jika Anda merujuk pada jawaban yang saat ini diterima, pernyataan pertama Anda salah. Destuktor tidak dipanggil sampai semua objek durasi penyimpanan statis dihancurkan.
Martin York
5

Berikut ini adalah implementasi yang mudah.

#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;
}

Hanya satu objek yang dibuat dan referensi objek ini dikembalikan setiap dan sesudahnya.

SingletonClass instance created!
00915CB8
00915CB8

Di sini 00915CB8 adalah lokasi memori Object singleton, sama untuk durasi program tetapi (biasanya!) Berbeda setiap kali program dijalankan.

NB Ini bukan thread yang aman. Anda harus memastikan keamanan thread.

Tunvir Rahman Tusher
sumber
5

Jika Anda ingin mengalokasikan objek di tumpukan, mengapa tidak menggunakan pointer unik. Memori juga akan dibatalkan alokasi karena kita menggunakan pointer unik.

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);
riderchap
sumber
3
Tidak digunakan lagi dalam c ++ 11. direkomendasikan unique_ptr. cplusplus.com/reference/memory/auto_ptr cplusplus.com/reference/memory/unique_ptr
Andrew
2
Ini bukan utas yang aman. Lebih baik untuk membuat m_slokal staticdari getInstance()dan menginisialisasi segera tanpa tes.
Galik
2

Memang mungkin dialokasikan dari tumpukan, tetapi tanpa sumber tidak ada cara untuk mengetahui.

Implementasi tipikal (diambil dari beberapa kode yang saya miliki di emacs) adalah:

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

... dan andalkan program keluar dari ruang lingkup untuk membersihkan sesudahnya.

Jika Anda bekerja pada platform di mana pembersihan harus dilakukan secara manual, saya mungkin akan menambahkan rutin pembersihan manual.

Masalah lain dengan melakukannya dengan cara ini adalah tidak aman-utas. Dalam lingkungan multithreaded, dua utas bisa melewati "jika" sebelum keduanya memiliki kesempatan untuk mengalokasikan contoh baru (jadi keduanya akan). Ini masih bukan masalah besar jika Anda mengandalkan penghentian program untuk membersihkan.

TED
sumber
Anda dapat menyimpulkan, karena Anda dapat melihat bahwa variabel instance adalah pointer ke instance kelas.
Artem Barger
3
Tidak perlu secara dinamis mengalokasikan singleton. Sebenarnya ini adalah ide yang buruk karena tidak ada cara untuk secara otomatis mengalokasikan menggunakan desain di atas. Biarkan itu keluar dari ruang lingkup tidak memanggil destruktor dan hanya malas.
Martin York
Anda dapat secara otomatis membatalkan alokasi menggunakan fungsi atexit. Itulah yang kami lakukan (tidak mengatakan itu ide yang baik)
Joe
2

Adakah yang menyebutkan std::call_oncedan std::once_flag? Sebagian besar pendekatan lain - termasuk penguncian ganda diperiksa - rusak.

Salah satu masalah utama dalam penerapan pola tunggal adalah inisialisasi yang aman. Satu-satunya cara yang aman adalah untuk menjaga urutan inisialisasi dengan hambatan sinkronisasi. Tetapi hambatan itu sendiri harus dimulai dengan aman. std::once_flagadalah mekanisme untuk mendapatkan inisialisasi aman yang dijamin.

Merah
sumber
2

Kami membahas topik ini baru-baru ini di kelas EECS saya. Jika Anda ingin melihat catatan kuliah secara detail, kunjungi http://umich.edu/~eecs381/lecture/IdiomsDesPattsCreational.pdf

Ada dua cara yang saya tahu untuk membuat kelas Singleton dengan benar.

Cara pertama:

Terapkan itu mirip dengan cara Anda memilikinya dalam contoh Anda. Sedangkan untuk penghancuran, "Lajang biasanya bertahan lama selama program dijalankan; sebagian besar OS akan memulihkan memori dan sebagian besar sumber daya lainnya ketika sebuah program berakhir, sehingga ada argumen untuk tidak khawatir tentang hal ini."

Namun, adalah praktik yang baik untuk membersihkan pada penghentian program. Oleh karena itu, Anda dapat melakukan ini dengan kelas SingletonDestructor statis tambahan dan menyatakannya sebagai teman di Singleton Anda.

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 akan dibuat pada startup program, dan "ketika program berakhir, semua objek global / statis dihancurkan oleh kode shutdown perpustakaan runtime (dimasukkan oleh linker), sehingga the_destroyer akan dihancurkan; destructor-nya akan menghapus Singleton, menjalankan destruktor. "

Jalan Kedua

Ini disebut Singleton Meyers, dibuat oleh penyihir C ++ Scott Meyers. Cukup tentukan get_instance () secara berbeda. Sekarang Anda juga dapat menyingkirkan variabel anggota penunjuk.

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

Ini rapi karena nilai yang dikembalikan adalah dengan referensi dan Anda dapat menggunakan .sintaks alih-alih ->mengakses variabel anggota.

"Kompiler secara otomatis membuat kode yang menciptakan 's' pertama kali melalui deklarasi, tidak setelahnya, dan kemudian menghapus objek statis pada penghentian program."

Perhatikan juga bahwa dengan Meyers Singleton Anda "dapat masuk ke situasi yang sangat sulit jika objek saling bergantung pada saat penghentian - kapan Singleton menghilang relatif terhadap objek lain? Tetapi untuk aplikasi sederhana, ini berfungsi dengan baik."

Tony Bai
sumber
1

Selain diskusi lain di sini, mungkin perlu dicatat bahwa Anda dapat memiliki ke-global-an, tanpa membatasi penggunaan pada satu contoh. Sebagai contoh, pertimbangkan kasus referensi menghitung sesuatu ...

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

Sekarang di suatu tempat di dalam suatu fungsi (seperti main) yang dapat Anda lakukan:

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

Wasit tidak perlu menyimpan pointer kembali ke masing-masing Storekarena informasi tersebut diberikan pada waktu kompilasi. Anda juga tidak perlu khawatir tentang masa Storepakai karena kompiler mengharuskan itu bersifat global. Jika memang hanya ada satu contoh Storemaka tidak ada overhead dalam pendekatan ini; dengan lebih dari satu contoh terserah kompiler untuk menjadi pintar tentang pembuatan kode. Jika perlu, ItemRefkelas bahkan dapat membuat frienddariStore (Anda dapat memiliki teman templated!).

Jika Storeitu sendiri adalah kelas templated maka hal-hal menjadi berantakan, tetapi masih mungkin untuk menggunakan metode ini, mungkin dengan menerapkan kelas pembantu dengan tanda tangan berikut:

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>. */ };

Pengguna sekarang dapat membuat StoreWrapperjenis (dan instance global) untuk setiap Storeinstance global , dan selalu mengakses toko melalui instance wrapper mereka (dengan demikian melupakan detail berdarah parameter templat yang diperlukan untuk menggunakan Store).

dan-man
sumber
0

Ini tentang manajemen objek seumur hidup. Misalkan Anda memiliki lebih dari lajang dalam perangkat lunak Anda. Dan mereka bergantung pada Logger singleton. Selama penghancuran aplikasi, misalkan objek singleton lain menggunakan Logger untuk mencatat langkah-langkah penghancurannya. Anda harus menjamin bahwa Logger harus dibersihkan terakhir. Oleh karena itu, harap periksa juga makalah ini: http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf

baris.aydinoz
sumber
0

Implementasi saya mirip dengan Galik. Perbedaannya adalah implementasi saya memungkinkan pointer bersama untuk membersihkan memori yang dialokasikan, sebagai kebalikan dari memegang memori hingga aplikasi keluar dan pointer statis dibersihkan.

#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;
Kevin Marshall
sumber
0

Kode Anda benar, kecuali bahwa Anda tidak mendeklarasikan pointer instance di luar kelas . Deklarasi kelas statis variabel statis tidak dianggap deklarasi dalam C ++, namun ini diizinkan dalam bahasa lain seperti C # atau Java dll.

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

Anda harus tahu bahwa instance Singleton tidak perlu dihapus secara manual oleh kami . Kita memerlukan satu objek dari itu di seluruh program, jadi pada akhir pelaksanaan program, itu akan secara otomatis dialokasikan.

Ali Sajjad
sumber
-1

Makalah yang dikaitkan dengan di atas menjelaskan kekurangan dari penguncian diperiksa ganda adalah bahwa kompiler dapat mengalokasikan memori untuk objek dan mengatur pointer ke alamat memori yang dialokasikan, sebelum konstruktor objek telah dipanggil. Namun sangat mudah dalam c ++ untuk menggunakan pengalokasi untuk mengalokasikan memori secara manual, dan kemudian menggunakan panggilan konstruk untuk menginisialisasi memori. Menggunakan appraoch ini, penguncian diperiksa dua kali berfungsi dengan baik.

sdfsdaf
sumber
2
Sayangnya tidak. Ini telah dibahas secara mendalam oleh beberapa pengembang C ++ terbaik di luar sana. Penguncian diperiksa ganda rusak di C ++ 03.
Martin York
-1
#define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;}

Contoh:

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

    public:
        INS(CCtrl);
Gank
sumber
-1

Kelas singleton sederhana, Ini harus menjadi file kelas tajuk Anda

#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

Akses singleton Anda seperti ini:

sSingletonClass->Relocate(1, 2, 5);
Ali Khazaee
sumber
-3

Saya pikir Anda harus menulis fungsi statis di mana objek statis Anda dihapus. Anda harus memanggil fungsi ini ketika Anda akan menutup aplikasi Anda. Ini akan memastikan Anda tidak mengalami kebocoran memori.

Yogi
sumber