ปิดใช้งานตัวสร้างสำเนา


173

ฉันมีชั้นเรียน:

class SymbolIndexer {
protected:
  SymbolIndexer ( ) { }

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

ฉันจะแก้ไขเพื่อปิดการใช้งานรหัสเช่น:

SymbolIndexer symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

และอนุญาตเฉพาะรหัสเช่น:

SymbolIndexer & ref_symbol_indexer_ = SymbolIndexer::GetUniqueInstance ( );

1
Btw นี่คือซิงเกิลที่มีบทบัญญัติสำหรับการสืบทอด (ได้รับการป้องกัน) หรือไม่
R. Martinho Fernandes

ฉันมีข้อสงสัยในรหัสของคุณทุกครั้งที่มีการสร้างอินสแตนซ์ที่แตกต่างกันฉันคิดว่า GetUniqueInstance () จะให้การอ้างอิงกับวัตถุเดียวกันเสมอ
Pratham Shah

คำตอบ:


286

คุณสามารถกำหนดให้ตัวสร้างสำเนาเป็นส่วนตัวและไม่ต้องติดตั้งได้:

private:
    SymbolIndexer(const SymbolIndexer&);

หรือใน C ++ 11 ห้ามอย่างชัดเจน:

SymbolIndexer(const SymbolIndexer&) = delete;

43
เกี่ยวกับdeleteคำหลักที่ฉันต้องการเพิ่มต่อไปนี้ นิสัยของฉันในปัจจุบันเมื่อออกแบบคลาสใหม่คือให้deleteทั้งตัวสร้างสำเนาและตัวดำเนินการกำหนดทันที ฉันพบว่าขึ้นอยู่กับบริบทส่วนใหญ่ไม่จำเป็นและการลบออกจะป้องกันบางกรณีของพฤติกรรมที่ไม่คาดคิด หากสถานการณ์เกิดขึ้นที่อาจจำเป็นต้องคัดลอก ctor ให้พิจารณาว่าสามารถทำได้ด้วยซีแมนทิกส์การย้ายหรือไม่ หากสิ่งนี้ไม่เป็นที่พึงปรารถนาให้จัดเตรียมการดำเนินการสำหรับทั้งสอง (!) ตัวคัดลอก ctor และตัวดำเนินการที่ได้รับมอบหมาย ไม่ว่าจะเป็นวิธีการที่ดีฉันจะปล่อยให้ผู้อ่าน
pauluss86

1
@ pauluss86 ฉันชอบวิธีการของคุณ แต่ฉันจะไม่ทำอย่างเต็มที่เพราะฉันคิดว่าเวลาที่ใช้ในรูปแบบนี้มากกว่าเวลาที่บันทึกไว้โดยข้อผิดพลาดที่ป้องกันไว้ ฉันไม่อนุญาตให้คัดลอกทุกครั้งที่ไม่แน่ใจ
Tomáš Zato - Reinstate Monica

@ pauluss86 สิ่งนี้เป็นสิ่งที่ Rust สร้างขึ้น: การย้ายโดยค่าเริ่มต้น (และ const-by-default) มีประโยชน์มากในความคิดของฉัน
Kapichu

33

หากคุณไม่คำนึงถึงการสืบทอดหลาย ๆ อย่าง (มันก็ไม่เลวเลย) คุณสามารถเขียนคลาสแบบง่ายด้วยตัวสร้างการคัดลอกส่วนตัวและตัวดำเนินการที่ได้รับมอบหมายและเพิ่มคลาสย่อยด้วย:

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

class SymbolIndexer: public Indexer, public NonAssignable {
};

สำหรับ GCC จะให้ข้อความแสดงข้อผิดพลาดต่อไปนี้:

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

ฉันไม่แน่ใจว่านี่จะทำงานในคอมไพเลอร์ทุกครั้งอย่างไร มีคำถามที่เกี่ยวข้องแต่ยังไม่มีคำตอบ

UPD:

ใน C ++ 11 คุณสามารถเขียนNonAssignableคลาสดังนี้:

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

deleteสมาชิกป้องกันคำหลักจากการเริ่มต้นสร้างเพื่อให้พวกเขาไม่สามารถใช้ต่อไปในสมาชิกเริ่มต้นสร้างคลาสที่ได้รับของ การพยายามกำหนดให้ข้อผิดพลาดต่อไปนี้ใน 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 มีคลาสสำหรับวัตถุประสงค์เดียวกันอยู่แล้วฉันคิดว่ามันใช้งานได้ในลักษณะเดียวกัน คลาสถูกเรียกใช้boost::noncopyableและมีไว้เพื่อใช้ดังต่อไปนี้:

#include <boost/core/noncopyable.hpp>

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

ฉันขอแนะนำให้ติดกับโซลูชันของ Boost หากนโยบายโครงการของคุณอนุญาต ดูboost::noncopyableคำถามที่เกี่ยวข้องอีกข้อหนึ่งสำหรับข้อมูลเพิ่มเติม


ไม่เป็นอย่างนั้นNonAssignable(const NonAssignable &other);เหรอ?
Troyseph

ฉันคิดว่าคำถามนี้จะทำให้ upvotes มากขึ้นหากมีการปรับปรุงเป็นdeleteไวยากรณ์คำหลักC ++ 11
Tomáš Zato - Reinstate Monica

@ TomášZato: ความคิดคือการทำให้ตัวสร้างสำเนาและผู้ประกอบการที่ได้รับมอบหมายอยู่ แต่ส่วนตัว หากคุณdeleteพวกเขามันหยุดทำงาน (ฉันเพิ่งตรวจสอบ)
firegurafiku

@ TomášZato: อ่าขอโทษวิธีทดสอบของฉันผิดเล็กน้อย การลบก็ทำงานได้เช่นกัน จะอัปเดตคำตอบในหนึ่งนาที
firegurafiku

3
@Troyseph: const Class&และClass const&สวยเหมือนกัน สำหรับพอยน์เตอร์คุณอาจClass const * constพิมพ์ได้
firegurafiku

4

ทำให้เป็นSymbolIndexer( const SymbolIndexer& )ส่วนตัว หากคุณกำหนดให้กับการอ้างอิงคุณจะไม่คัดลอก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.