Nonaktifkan copy constructor

173

Saya ada kelas :

class SymbolIndexer {
protected:
  SymbolIndexer ( ) { }

public:
  static inline SymbolIndexer & GetUniqueInstance ( ) 
  { 
    static SymbolIndexer uniqueinstance_ ;
    return uniqueinstance_ ; 
  }
};

Bagaimana saya memodifikasinya untuk menonaktifkan kode seperti:

SymbolIndexer symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

dan hanya izinkan kode seperti:

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );
Debugger yang rendah hati
sumber
1
Btw, apakah ini singleton dengan ketentuan warisan (diberikan dilindungi)?
R. Martinho Fernandes
Saya memiliki keraguan dalam kode Anda akan setiap kali contoh yang berbeda dibuat Saya pikir GetUniqueInstance () akan selalu memberikan referensi ke objek yang sama.
Pratham Shah

Jawaban:

286

Anda dapat menjadikan konstruktor salinan sebagai pribadi dan tidak memberikan implementasi:

private:
    SymbolIndexer(const SymbolIndexer&);

Atau di C ++ 11, secara eksplisit melarangnya:

SymbolIndexer(const SymbolIndexer&) = delete;
R. Martinho Fernandes
sumber
43
Mengenai deletekata kunci saya ingin menambahkan yang berikut ini. Kebiasaan kebiasaan saya saat ini ketika merancang kelas baru adalah untuk deletemenyalin konstruktor dan operator penugasan segera. Saya telah menemukan bahwa, tergantung pada konteksnya, sebagian besar tidak perlu dan menghapusnya mencegah beberapa kasus perilaku yang tidak terduga. Jika suatu situasi terjadi di mana copy ctor mungkin diperlukan, tentukan apakah itu dapat dilakukan dengan memindahkan semantik. Jika ini tidak diinginkan, berikan implementasi untuk (!) Copy ctor dan operator penugasan. Apakah ini pendekatan yang baik, saya akan menyerahkan kepada pembaca.
pauluss86
1
@ pauluss86 Saya suka pendekatan Anda tetapi saya tidak akan sepenuhnya berkomitmen untuk itu karena saya pikir waktu yang dihabiskan mengikuti pola ini lebih besar daripada waktu yang disimpan oleh kesalahan yang mencegahnya. Saya hanya melarang menyalin kapan pun tidak yakin.
Tomáš Zato - Reinstate Monica
@ pauluss86 Ini pada dasarnya yang dilakukan Rust: Move-by-default (dan const-by-default). Sangat membantu menurut saya.
Kapichu
33

Jika Anda tidak keberatan multiple inheritance (tidak buruk, bagaimana pun juga), Anda dapat menulis kelas sederhana dengan private copy constructor dan operator penugasan dan juga menambahkannya:

class NonAssignable {
private:
    NonAssignable(NonAssignable const&);
    NonAssignable& operator=(NonAssignable const&);
public:
    NonAssignable() {}
};

class SymbolIndexer: public Indexer, public NonAssignable {
};

Untuk GCC ini memberikan pesan kesalahan berikut:

test.h: In copy constructor ‘SymbolIndexer::SymbolIndexer(const SymbolIndexer&)’:
test.h: error: ‘NonAssignable::NonAssignable(const NonAssignable&)’ is private

Saya tidak begitu yakin ini bekerja di setiap kompiler. Ada pertanyaan terkait , tetapi belum ada jawaban.

UPD:

Di C ++ 11 Anda juga dapat menulis NonAssignablekelas sebagai berikut:

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

Kata deletekunci mencegah anggota agar tidak dibangun secara default, sehingga tidak dapat digunakan lebih lanjut dalam anggota yang dibangun dengan standar bawaan. Mencoba menetapkan memberikan kesalahan berikut di GCC:

test.cpp: error: use of deleted function
          ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
test.cpp: note: ‘SymbolIndexer& SymbolIndexer::operator=(const SymbolIndexer&)’
          is implicitly deleted because the default definition would
          be ill-formed:

UPD:

Boost sudah memiliki kelas hanya untuk tujuan yang sama, saya kira itu bahkan diimplementasikan dengan cara yang sama. Kelas dipanggil boost::noncopyabledan dimaksudkan untuk digunakan sebagai berikut:

#include <boost/core/noncopyable.hpp>

class SymbolIndexer: public Indexer, private boost::noncopyable {
};

Saya akan merekomendasikan menempel pada solusi Boost jika kebijakan proyek Anda mengizinkannya. Lihat juga boost::noncopyablepertanyaan lain yang terkait untuk informasi lebih lanjut.

firegurafiku
sumber
Bukankah seharusnya begitu NonAssignable(const NonAssignable &other);?
Troyseph
Saya pikir pertanyaan ini akan mendapatkan lebih banyak upvote jika diperbarui ke deletesintaksis kata kunci C ++ 11 .
Tomáš Zato - Reinstate Monica
@ TomášZato: Idenya adalah untuk menjaga copy constructor dan operator penugasan tetap ada, tetapi bersifat pribadi. Jika Anda deletemereka, itu berhenti bekerja (saya baru saja memeriksa).
firegurafiku
@ TomášZato: Ah, maaf, metode pengujian saya agak salah. Menghapus juga berfungsi. Akan memperbarui jawabannya dalam satu menit.
firegurafiku
3
@Troyseph: const Class&dan Class const&sama saja. Untuk pointer Anda bisa Class const * constmengetik genap .
firegurafiku
4

Jadikan SymbolIndexer( const SymbolIndexer& )pribadi. Jika Anda menetapkan referensi, Anda tidak menyalin.

Aaron Klotz
sumber