ทำไมต้องลบตัวสร้างอย่างชัดเจน


95

เมื่อไร / เหตุใดฉันจึงต้องการลบตัวสร้างของฉันอย่างชัดเจน สมมติว่าเหตุผลคือเพื่อป้องกันการใช้งานทำไมไม่ทำให้มันprivate?

class Foo
{ 
  public: 
    Foo() = delete; 
};

14
มันเข้ากันได้ดีกับ= defaultชั้นเรียนไม่สามารถใช้งานได้และฉันชอบเห็นการใช้ฟังก์ชันที่ถูกลบเป็นการส่วนตัว over Function เป็นแบบส่วนตัว ในอดีตระบุอย่างชัดเจนว่า "สิ่งนี้ไม่ได้มีไว้สำหรับใช้" หากมีสิ่งใดออกมาแสดงว่าคลาสที่ไม่สามารถใช้งานได้จริงจะทำให้เกิดความแตกต่างทางความหมาย
chris

16
ฉันคิดตามตรงว่าผู้คนเริ่มไม่พอใจกับคะแนนโหวตที่ใกล้ชิด ฉันไม่เห็นว่ามันไม่สร้างสรรค์อย่างไร
Luchian Grigore

4
@LuchianGrigore: เห็นด้วย ฉันสงสัยว่าทำไมชุมชนถึงเข้มงวดกับตัวเองมากขึ้น ฉันไม่เห็นประเด็น
Ed S.

11
เนื่องจากฉันไม่ค่อยใช้ C ++ 11 สิ่งนี้จึงเป็นข้อมูลสำหรับฉันมากกว่าที่ OP อาจจะรู้ตัวด้วยซ้ำ ผมไม่ได้รู้ว่าคุณสามารถdeleteแท็กคอนสตรัคสำหรับ ทั้งคำถามและคำตอบของ Luchian ถือว่าสร้างสรรค์ได้อย่างง่ายดาย ใครก็ตามที่ไม่ได้หายใจประเด็นปลีกย่อยของ C ++ 11 แต่จะต้องทำในไม่ช้าจะได้รับบางสิ่งจากทั้งสองอย่าง
WhozCraig

คำตอบ:


89

เกี่ยวกับ:

//deleted constructor
class Foo
{ 
  public: 
    Foo() = delete;     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //illegal
}

เทียบกับ

//private constructor
class Foo
{ 
  private: 
    Foo() {}     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //legal
}

โดยพื้นฐานแล้วมันต่างกัน privateบอกคุณว่ามีเพียงสมาชิกในชั้นเรียนเท่านั้นที่สามารถเรียกใช้เมธอดนั้นหรือเข้าถึงตัวแปรนั้นได้ (หรือเพื่อน ๆ แน่นอน) ในกรณีนี้เป็นเรื่องถูกกฎหมายสำหรับstaticเมธอดของคลาสนั้น (หรือสมาชิกอื่น ๆ ) ในการเรียกตัวprivateสร้างของคลาส สิ่งนี้ไม่ถือสำหรับตัวสร้างที่ถูกลบ

ตัวอย่างที่นี่


3
คุณไม่จำเป็นต้องประกาศ Foo () เลยหากคุณประกาศ Foo (int) Foo () จะไม่ถูกสร้างขึ้นดังนั้น Foo f จึงไม่ถูกต้องอยู่ดี ดังนั้นตัวอย่างของคุณจึงไม่แสดงกรณีสำหรับตัวสร้างที่ถูกลบ ดูตัวเอง - ideone.com/mogiIF
ทำเครื่องหมาย

1
@ เครื่องหมายฉันเขียน 2 ตัวสร้างเพื่อพิสูจน์ประเด็น ฉันจะแก้ไขเพื่อให้ทุกคนชัดเจน
Luchian Grigore

1
ฉันเข้าใจความแตกต่างฉันไม่เข้าใจมูลค่าเพิ่มของคำสั่งลบโดยทั่วไปและสำหรับตัวสร้างโดยเฉพาะ ท้ายที่สุดฉันสามารถระบุตัวสร้างเริ่มต้นส่วนตัวโดยไม่มีเนื้อความ จากนั้นรหัสจะล้มเหลวในระหว่างการเชื่อมโยงเท่านั้น ฉันเห็นว่าการลบนั้นบ่งบอกถึงเจตนาที่ชัดเจนกว่า แต่ก็เกี่ยวกับเรื่องนี้
ทำเครื่องหมาย

11
@mark ใช่นั่นจะเป็นวิธี C ++ 98 ในการทำสิ่งต่างๆ แต่ IMHO การถ่ายทอดความตั้งใจอย่างชัดเจนเป็นสิ่งที่สำคัญมากในการเขียนโปรแกรมโดยทั่วไป ในกรณีนี้ผู้อ่านบางคนอาจเห็นตัวสร้างที่ไม่ได้กำหนดส่วนตัวและคิดว่ามันเป็นเรื่องบังเอิญและเพียงแค่เพิ่มคำจำกัดความโดยเฉพาะอย่างยิ่งหากคำจำกัดความนั้นไม่สำคัญเท่ากับตัวสร้างเริ่มต้น (ใช่การมีความคิดเห็นช่วยได้ แต่เราต้องการคอมไพเลอร์มากกว่า - การบังคับใช้มากกว่าการบังคับใช้ความคิดเห็น) เรายังได้รับข้อความแสดงข้อผิดพลาดที่ดีกว่ามากซึ่งระบุว่า "ลบอย่างชัดเจน" แทนที่จะเป็น "ข้อมูลอ้างอิงที่ไม่ได้กำหนด"
mpark

2
ฉันไม่เข้าใจว่าสิ่งนี้ตอบคำถามหลักได้อย่างไร คำถามในชื่อเรื่องและคำถามแรกของ OP ในโพสต์คือเมื่อใด / ทำไมฉันจึงต้องการลบตัวสร้างของฉันอย่างชัดเจน
Alexander Bolinsky

13

ทำไมต้องลบตัวสร้างอย่างชัดเจน

อีกเหตุผลหนึ่ง:
ฉันใช้deleteเมื่อต้องการรับรองว่าคลาสถูกเรียกด้วยตัวเริ่มต้น ฉันถือว่าเป็นวิธีที่ดีมากในการบรรลุสิ่งนี้โดยไม่ต้องตรวจสอบรันไทม์

คอมไพเลอร์ C ++ จะตรวจสอบให้คุณ

class Foo
{
   public:
       Foo() = delete;
       Foo(int bar) : m_bar(bar) {};
   private:
       int m_bar;
}

รหัสนี้ - ง่ายมากทำให้มั่นใจได้ว่าไม่มีการสร้างอินสแตนซ์เช่นนี้:Foo foo;


12
ที่นี่ไม่จำเป็นต้องมีการประกาศที่ถูกลบ มันถูกลบโดยอัตโนมัติด้วยตัวสร้างที่ผู้ใช้จัดหาให้
Mike Lui

5
เพื่อชี้แจงความคิดเห็นโดย @MikeLui ที่ประกาศลบไม่จำเป็นที่จะคอมไพเลอร์ มีหลายกรณีเมื่อรหัสเช่นนี้ควรจะรวมอยู่ในการประกาศความตั้งใจที่จะเขียนโปรแกรมอื่น
Jeff G

นอกเหนือจากการประกาศเจตจำนงของคุณแล้วยังสร้างสถานที่ที่ชัดเจนในการบันทึกเหตุผลของคุณในการลบในอินเทอร์เฟซสาธารณะและนอกจากนี้ข้อผิดพลาดของคอมไพเลอร์จะเป็นข้อความสั้น ๆ เช่น "การใช้ฟังก์ชันที่ถูกลบ" หากFooมีตัวสร้างจำนวนมากไม่ใช่แค่ค่าดีฟอลต์ก็Foo foo;จะทำให้เกิดข้อผิดพลาดอีกต่อไปในการแสดงรายการคอนสตรัคเตอร์ที่กำหนดโดยนัยป้องกันและแบบส่วนตัวทั้งหมดที่ไม่สามารถจับคู่ได้
sigma

ฉันยังไม่เข้าใจว่าบรรทัดพิเศษที่มีการประกาศตัวสร้างที่มี "= ลบ" คำหลักประกาศเจตนาของแนวคิด "ไม่มีตัวสร้างเริ่มต้น" ดีกว่า ... เพียงแค่ไม่มีตัวสร้างเริ่มต้น? ตัวอย่าง: ฉันไม่ต้องการประกาศตัวแปร "a" ในโค้ดของฉัน - อะไรจะดีไปกว่าการเขียน "// int a; // ไม่จำเป็นต้องกำหนดตัวแปร a" หรือไม่ต้องเขียนอะไรเกี่ยวกับตัวแปรนี้ในโค้ด
Ezh

2

ฉันได้พบกับ ctors เริ่มต้นที่ประกาศเป็น 'ลบ' ในซอร์สโค้ดของ LLVM (ใน AlignOf.h เป็นต้น) เทมเพลตคลาสที่เกี่ยวข้องมักจะอยู่ในเนมสเปซพิเศษที่เรียกว่า 'llvm :: detail' จุดประสงค์ทั้งหมดที่ฉันคิดคือพวกเขาถือว่าคลาสนั้นเป็นคลาสผู้ช่วยเหลือเท่านั้น พวกเขาไม่เคยตั้งใจที่จะยกตัวอย่างพวกเขา เท่านั้นที่จะใช้ภายในบริบทของเทมเพลตคลาสอื่น ๆ ด้วยเทคนิคการเขียนโปรแกรมเมตาโปรแกรมที่ทำงานในเวลาคอมไพล์

เช่น. มีเทมเพลตคลาส AlignmentCalcImpl ซึ่งใช้เฉพาะในเทมเพลตคลาสอื่นที่เรียกว่า AlignOf เป็นพารามิเตอร์สำหรับตัวดำเนินการ sizeof (.) นิพจน์นั้นสามารถประเมินได้ในเวลาคอมไพล์ และไม่จำเป็นต้องสร้างอินสแตนซ์เทมเพลต -> ดังนั้นทำไมไม่ประกาศลบ ctor เริ่มต้นเพื่อแสดงความตั้งใจนี้

แต่มันเป็นเพียงข้อสันนิษฐานของฉัน

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