เมื่อฉันเรียนรู้ C ++ นานมาแล้วมันเน้นอย่างมากกับฉันว่าส่วนหนึ่งของจุดของ C ++ คือเหมือนลูปมี "loop-invariants" คลาสยังมีค่าคงที่ที่เกี่ยวข้องกับอายุการใช้งานของวัตถุ - สิ่งที่ควรเป็นจริง เพราะตราบเท่าที่วัตถุยังมีชีวิตอยู่ สิ่งที่ควรกำหนดโดยผู้สร้างและเก็บรักษาไว้โดยวิธีการ การห่อหุ้ม / การควบคุมการเข้าถึงมีไว้เพื่อช่วยให้คุณบังคับใช้ค่าคงที่ Raii เป็นสิ่งหนึ่งที่คุณสามารถทำได้กับความคิดนี้
ตั้งแต่ C ++ 11 ตอนนี้เรามีความหมายย้าย สำหรับคลาสที่รองรับการย้ายการย้ายจากวัตถุไม่สิ้นสุดอย่างเป็นทางการตลอดอายุการย้ายควรจะปล่อยให้มันอยู่ในสถานะ "ถูกต้อง" บางอย่าง
ในการออกแบบชั้นเรียนเป็นเรื่องที่ไม่ถูกต้องหรือไม่หากคุณออกแบบชั้นเรียนเพื่อให้ค่าคงที่ของชั้นเรียนถูกเก็บรักษาไว้จนถึงจุดที่ถูกย้ายออกไป หรือว่าไม่เป็นไรถ้ามันจะช่วยให้คุณทำให้มันเร็วขึ้น
หากต้องการทำให้เป็นรูปธรรมสมมติว่าฉันมีประเภททรัพยากรที่ไม่สามารถคัดลอกได้ แต่เคลื่อนย้ายได้เช่น:
class opaque {
opaque(const opaque &) = delete;
public:
opaque(opaque &&);
...
void mysterious();
void mysterious(int);
void mysterious(std::vector<std::string>);
};
และด้วยเหตุผลใดก็ตามฉันจำเป็นต้องทำ wrapper ที่คัดลอกได้สำหรับวัตถุนี้เพื่อให้สามารถใช้ได้บางทีในระบบการแจกจ่ายที่มีอยู่บางระบบ
class copyable_opaque {
std::shared_ptr<opaque> o_;
copyable_opaque() = delete;
public:
explicit copyable_opaque(opaque _o)
: o_(std::make_shared<opaque>(std::move(_o)))
{}
void operator()() { o_->mysterious(); }
void operator()(int i) { o_->mysterious(i); }
void operator()(std::vector<std::string> v) { o_->mysterious(v); }
};
ในcopyable_opaque
วัตถุนี้ค่าคงที่ของคลาสที่สร้างขึ้นในการก่อสร้างคือสมาชิกo_
จะชี้ไปยังวัตถุที่ถูกต้องเสมอเนื่องจากไม่มี ctor เริ่มต้นและ ctor เดียวที่ไม่ใช่ ctor คัดลอกรับประกันเหล่านี้ ทั้งหมดของoperator()
วิธีการคิดว่าคงนี้ถือและรักษามันไว้หลังจากนั้น
อย่างไรก็ตามหากวัตถุถูกย้ายจากนั้นo_
จะชี้ไปที่ไม่มีอะไร และหลังจากนั้นการเรียกเมธอดใด ๆoperator()
จะทำให้ UB / a เกิดขัดข้อง
หากวัตถุนั้นไม่เคยถูกเคลื่อนย้ายจากนั้นค่าคงที่จะถูกเก็บรักษาไว้จนถึงการเรียก dtor
สมมุติว่าสมมุติว่าฉันเขียนคลาสนี้และอีกหลายเดือนต่อมาผู้ร่วมงานในจินตนาการของฉันได้รับประสบการณ์ UB เพราะในการทำงานที่ซับซ้อนบางอย่างที่วัตถุเหล่านี้จำนวนมากถูกสับไปรอบ ๆ ด้วยเหตุผลบางอย่างเขาย้ายจากสิ่งเหล่านี้ วิธีการ เห็นได้ชัดว่ามันเป็นความผิดของเขาในตอนท้ายของวัน แต่ชั้นนี้ "ออกแบบมาไม่ดี"
ความคิด:
ปกติแล้วมันจะเป็นรูปแบบที่ไม่ดีใน C ++ เพื่อสร้างวัตถุซอมบี้ที่จะระเบิดหากคุณแตะมัน
หากคุณไม่สามารถสร้างวัตถุบางอย่างไม่สามารถสร้างค่าคงที่แล้วโยนข้อยกเว้นจาก ctor หากคุณไม่สามารถรักษาค่าคงที่ในวิธีการบางอย่างให้ส่งสัญญาณข้อผิดพลาดอย่างใดอย่างหนึ่งและย้อนกลับ สิ่งนี้ควรแตกต่างจากการย้ายจากวัตถุหรือไม่?มันเพียงพอหรือไม่ที่จะจัดทำเอกสาร "หลังจากวัตถุนี้ถูกย้ายไปมันผิดกฎหมาย (UB) ที่จะทำอะไรกับมันนอกจากทำลายมัน" ในส่วนหัว?
เป็นการดีกว่าที่จะยืนยันอย่างต่อเนื่องว่าใช้ได้ในการเรียกใช้แต่ละวิธีหรือไม่
ชอบมาก
class copyable_opaque {
std::shared_ptr<opaque> o_;
copyable_opaque() = delete;
public:
explicit copyable_opaque(opaque _o)
: o_(std::make_shared<opaque>(std::move(_o)))
{}
void operator()() { assert(o_); o_->mysterious(); }
void operator()(int i) { assert(o_); o_->mysterious(i); }
void operator()(std::vector<std::string> v) { assert(o_); o_->mysterious(v); }
};
การยืนยันไม่ได้ปรับปรุงพฤติกรรมอย่างมีนัยสำคัญและจะทำให้ช้าลง หากโครงการของคุณใช้รูปแบบ "build build / debug build" แทนที่จะใช้กับการยืนยันเสมอฉันคิดว่านี่น่าสนใจกว่าเนื่องจากคุณไม่ต้องจ่ายเงินสำหรับเช็คใน build build ถ้าคุณไม่มี debug builds จริง ๆ แล้วมันก็ไม่น่าสนใจ
- เป็นการดีกว่าหรือที่จะทำให้คลาสคัดลอกได้ แต่ไม่สามารถย้ายได้
สิ่งนี้ดูเหมือนว่าจะไม่ดีและทำให้เกิดผลการทำงาน แต่ก็แก้ไขปัญหา "ไม่แปรเปลี่ยน" ได้อย่างตรงไปตรงมา
คุณคิดว่าอะไรคือ "แนวทางปฏิบัติที่ดีที่สุด" ที่เกี่ยวข้องที่นี่