คำหลัก 'ไม่แน่นอน' มีวัตถุประสงค์อื่นนอกเหนือจากการอนุญาตให้แก้ไขตัวแปรโดยฟังก์ชัน const หรือไม่


527

ไม่นานมานี้ฉันพบรหัสบางอย่างที่ทำเครื่องหมายตัวแปรสมาชิกของคลาสด้วยmutableคำหลัก เท่าที่ฉันเห็นมันก็ช่วยให้คุณสามารถปรับเปลี่ยนตัวแปรในconstวิธีการ:

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

นี่เป็นเพียงการใช้คำหลักนี้เท่านั้นหรือมากกว่าที่จะเป็นไปตามตา? ฉันได้ใช้เทคนิคนี้ในชั้นเรียนทำเครื่องหมายboost::mutexเป็นconstฟังก์ชั่นที่ไม่อนุญาตให้ล็อคเพื่อเหตุผลด้านความปลอดภัยของเธรด แต่ตามจริงแล้วมันรู้สึกเหมือนแฮ็คเล็กน้อย


2
แม้ว่าคำถามหากคุณไม่ได้แก้ไขอะไรทำไมคุณต้องใช้ mutex ตั้งแต่แรก? ฉันแค่อยากจะเข้าใจสิ่งนี้
แปลความผิดพลาด

@ การแก้ไขข้อผิดพลาดที่คุณกำลังแก้ไขบางสิ่งคุณเพียงแค่ควบคุมว่าใคร / สามารถทำการแก้ไขผ่านทาง const ได้ ตัวอย่างไร้เดียงสาจริง ๆ ลองนึกภาพว่าถ้าฉันให้มือจับที่ไม่ใช่ const กับเพื่อนศัตรูจะได้รับด้ามจับ const เพื่อนสามารถปรับเปลี่ยนศัตรูไม่สามารถ
iheanyi

1
หมายเหตุ: นี่เป็นตัวอย่างที่ดีของการใช้คำหลักmutable: stackoverflow.com/questions/15999123/…
Gabriel Staples

ฉันหวังว่ามันจะถูกใช้เพื่อแทนที่const(ประเภท) ดังนั้นฉันไม่จำเป็นต้องทำเช่นนี้: class A_mutable{}; using A = A_mutable const; mutable_t<A> a;ถ้าฉันต้องการ const-by-default เช่นmutable A a;(ชัดเจนไม่แน่นอน) และA a;(impl const const)
alfC

คำตอบ:


351

จะช่วยให้การแยกความแตกต่างของค่าบิตและค่าตรรกะ Log แบบตรรกะคือเมื่อวัตถุไม่เปลี่ยนแปลงในลักษณะที่สามารถมองเห็นได้ผ่านส่วนต่อประสานสาธารณะเช่นตัวอย่างการล็อกของคุณ อีกตัวอย่างหนึ่งคือคลาสที่คำนวณค่าในครั้งแรกที่มีการร้องขอและแคชผลลัพธ์

เนื่องจาก c ++ 11 mutableสามารถใช้กับแลมบ์ดาเพื่อแสดงว่าสิ่งต่าง ๆ ที่ถูกจับด้วยค่าสามารถแก้ไขได้ (โดยค่าเริ่มต้นไม่ใช่):

int x = 0;
auto f1 = [=]() mutable {x = 42;};  // OK
auto f2 = [=]()         {x = 42;};  // Error: a by-value capture cannot be modified in a non-mutable lambda

52
'ไม่แน่นอน' จะไม่ส่งผลกระทบต่อค่าคงที่บิต / ตรรกะ C ++ เป็นเพียง const บิตและ 'แน่นอน' คำหลักที่สามารถนำมาใช้เพื่อยกเว้นสมาชิกจากการตรวจสอบนี้ ไม่สามารถบรรลุ const 'ตรรกะ' ใน C ++ นอกเหนือจาก abstractions (เช่น SmartPtrs)
Richard Corden

111
@ Richard: คุณไม่มีจุด ไม่มีคีย์เวิร์ด "ตรรกะ const" จริง แต่มันเป็นความแตกต่างทางความคิดที่โปรแกรมเมอร์ตัดสินใจว่าควรแยกสมาชิกใดโดยการทำให้เกิดความไม่แน่นอนขึ้นอยู่กับความเข้าใจในสิ่งที่ถือเป็นสถานะตรรกะที่สังเกตได้ของวัตถุ
Tony Delroy

6
@ jay ใช่นั่นคือจุดทั้งหมดของ markig ตัวแปรสมาชิกที่ไม่แน่นอนเพื่อให้สามารถเปลี่ยนแปลงได้ในวัตถุ const
KeithB

6
ทำไมคนเราถึงไม่อยากเปลี่ยนเป็น lambdas? มันจะไม่เพียงพอที่จะจับตัวแปรโดยอ้างอิง?
จอร์โจ

11
@Giorgio: ความแตกต่างคือการแก้ไขxภายในแลมบ์ดายังคงอยู่ภายในแลมบ์ดานั่นคือฟังก์ชั่นแลมบ์ดาสามารถแก้ไขสำเนาของตัวเองxได้เท่านั้น ไม่สามารถมองเห็นการเปลี่ยนแปลงภายนอกภาพต้นฉบับxยังคงไม่เปลี่ยนแปลง พิจารณาว่า lambdas ถูกนำไปใช้เป็นคลาส functor; ตัวแปรที่ถูกจับสอดคล้องกับตัวแปรสมาชิก
เซบาสเตียนมัค

138

mutableคำหลักเป็นวิธีที่จะแทงที่constม่านผ้าม่านคุณมากกว่าวัตถุของคุณ หากคุณมีการอ้างอิง const หรือชี้ไปยังวัตถุที่คุณไม่สามารถแก้ไขวัตถุนั้นในทางใด ๆยกเว้นmutableเมื่อใดและวิธีการที่จะมีการทำเครื่องหมาย

ด้วยconstการอ้างอิงหรือตัวชี้ของคุณคุณจะถูก จำกัด :

  • เข้าถึงการอ่านสำหรับสมาชิกข้อมูลที่มองเห็นได้เท่านั้น
  • constสิทธิ์ในการเรียกวิธีการเดียวที่จะทำเครื่องหมายเป็น

ยกเว้นทำให้มันดังนั้นตอนนี้คุณสามารถเขียนหรือชุดข้อมูลสมาชิกที่ถูกทำเครื่องหมายmutable mutableนั่นคือความแตกต่างที่มองเห็นจากภายนอกเท่านั้น

ภายในเหล่านั้นวิธีการที่จะเห็นคุณยังสามารถเขียนข้อมูลสมาชิกที่ถูกทำเครื่องหมายconst mutableโดยพื้นฐานแล้วม่าน const จะถูกเจาะอย่างละเอียด มันขึ้นอยู่กับผู้ออกแบบ API อย่างสมบูรณ์เพื่อให้แน่ใจว่าmutableจะไม่ทำลายconstแนวคิดและใช้ในกรณีพิเศษที่มีประโยชน์เท่านั้น mutableคำหลักที่จะช่วยเพราะมันได้อย่างชัดเจนข้อมูลสมาชิกเครื่องหมายที่อยู่ภายใต้กรณีพิเศษเหล่านี้

ในทางปฏิบัติคุณสามารถใช้constอย่างย่ำแย่ตลอด codebase ของคุณ (โดยพื้นฐานแล้วคุณต้องการ "ติดเชื้อ" codebase ของคุณด้วยconst"โรค") ในโลกนี้พอยน์เตอร์และการอ้างอิงมีconstข้อยกเว้นน้อยมากการให้โค้ดที่ง่ายต่อการให้เหตุผลและเข้าใจ สำหรับการพูดนอกเรื่องที่น่าสนใจค้นหา "ความโปร่งใสอ้างอิง"

ในที่สุดหากไม่มีmutableคำหลักคุณจะถูกบังคับให้ใช้const_castเพื่อจัดการกับกรณีพิเศษต่าง ๆ ที่มีประโยชน์ซึ่งอนุญาต (การแคชการอ้างอิงการนับการดีบักข้อมูล ฯลฯ ) น่าเสียดายconst_castยิ่งกว่าการทำลายmutableเพราะมันบังคับให้ลูกค้า API ทำลายการconstป้องกันของวัตถุที่เขาใช้ นอกจากนี้ยังทำให้เกิดการconstทำลายอย่างกว้างขวาง: const_castการใช้ตัวชี้ const หรือการอ้างอิงช่วยให้การเขียนและวิธีการเข้าถึงสมาชิกที่มองเห็นได้อย่างอิสระ ในทางตรงกันข้ามmutableผู้ออกแบบ API ต้องใช้การควบคุมที่ละเอียดยิ่งกว่าconstข้อยกเว้นและโดยปกติแล้วข้อยกเว้นเหล่านี้จะถูกซ่อนอยู่ในconstวิธีการดำเนินการกับข้อมูลส่วนตัว

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


8
นอกจากนี้การใช้const_castเพื่อปรับเปลี่ยนส่วนหนึ่งของconstวัตถุทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด
Brian

ผมไม่เห็นด้วยกับการเพราะมันบังคับให้ลูกค้า API เพื่อทำลายการป้องกัน const ของวัตถุ หากคุณกำลังใช้const_castในการดำเนินการกลายพันธุ์ของตัวแปรสมาชิกในconstวิธีการที่คุณจะไม่ขอให้ลูกค้าไปทำหล่อ - คุณจะทำมันได้ภายในวิธีการโดยไอเอ็นจีconst_cast thisโดยทั่วไปจะช่วยให้คุณหลีกเลี่ยงความไม่ลงรอยกันของสมาชิกโดยพลการในไซต์การโทรที่เฉพาะเจาะจงในขณะที่mutableให้คุณลบ const ในสมาชิกที่เฉพาะเจาะจงในทุกไซต์การโทร หลังมักจะเป็นสิ่งที่คุณต้องการสำหรับการใช้งานทั่วไป (แคชสถิติ) แต่บางครั้ง const_cast เหมาะกับรูปแบบ
BeeOnRope

1
const_castรูปแบบที่ไม่เหมาะสมดีในบางกรณีเช่นเมื่อคุณต้องการแก้ไขชั่วคราวเป็นสมาชิกแล้วเรียกคืนได้ (สวยมากเช่นboost::mutex) วิธีการดังกล่าวมีค่าคงที่ทางตรรกะเนื่องจากสถานะสุดท้ายเหมือนกันกับค่าเริ่มต้น แต่คุณต้องการเปลี่ยนแปลงชั่วคราว const_castมีประโยชน์เพราะมันช่วยให้คุณทิ้ง const โดยเฉพาะในวิธีการนี้หากคุณกลายพันธุ์จะถูกยกเลิก แต่mutableจะไม่เหมาะสมเพราะมันจะลบการป้องกัน const จากวิธีการทั้งหมดซึ่งไม่จำเป็นต้องทำตาม "ทำ , เลิกทำ "รูปแบบ
BeeOnRope

2
ตำแหน่งที่เป็นไปได้ของวัตถุที่กำหนด const ไว้ในหน่วยความจำแบบอ่านอย่างเดียว (โดยทั่วไปหน่วยความจำที่ทำเครื่องหมายเป็นแบบอ่านอย่างเดียว) และภาษามาตรฐานที่เกี่ยวข้องซึ่งทำให้สิ่งนี้ทำให้const_castเกิดระเบิดเวลาได้ mutableไม่มีปัญหาดังกล่าวเนื่องจากไม่สามารถวางวัตถุดังกล่าวในหน่วยความจำแบบอ่านอย่างเดียว
BeeOnRope

75

การใช้งานพร้อม boost :: mutex ของคุณเป็นสิ่งที่คำหลักนี้มีไว้สำหรับ การใช้งานอื่นสำหรับการแคชผลลัพธ์ภายในเพื่อเพิ่มความเร็วในการเข้าถึง

โดยทั่วไป 'ไม่แน่นอน' ใช้กับแอตทริบิวต์ class ใด ๆ ที่ไม่ส่งผลกระทบต่อสถานะที่มองเห็นจากภายนอกของวัตถุ

ในโค้ดตัวอย่างในคำถามของคุณการเปลี่ยนแปลงอาจไม่เหมาะสมหากค่าของ done_ ส่งผลกระทบต่อสถานะภายนอกขึ้นอยู่กับสิ่งที่อยู่ใน ... ; ส่วนหนึ่ง


35

ไม่แน่นอนสำหรับการทำเครื่องหมายแอตทริบิวต์ที่ระบุว่าสามารถแก้ไขได้จากภายในconstวิธีการ นั่นคือจุดประสงค์ของมันเท่านั้น mutableคิดอย่างรอบคอบก่อนที่จะใช้มันเพราะรหัสของคุณอาจจะทำความสะอาดและสามารถอ่านได้มากขึ้นถ้าคุณเปลี่ยนการออกแบบมากกว่าการใช้งาน

http://www.highprogrammer.com/alan/rants/mutable.html

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

ตัวอย่างผู้เขียนให้รวมถึงการแคชและตัวแปรการดีบักชั่วคราว


2
ฉันคิดว่าลิงก์นี้ให้ตัวอย่างที่ดีที่สุดของสถานการณ์ที่การเปลี่ยนแปลงไม่ได้มีประโยชน์มันเกือบจะปรากฏว่ามันใช้สำหรับการดีบักเท่านั้น (ต่อการใช้งานที่ถูกต้อง)
กระตือรือร้น

การใช้งานของmutableรหัสสามารถทำให้อ่านง่ายขึ้นและสะอาดขึ้น ในตัวอย่างต่อไปนี้readสามารถเป็นไปconstตามที่คาดไว้ `m_mutex ที่ไม่แน่นอน คอนเทนเนอร์ m_container เพิ่มเป็นโมฆะ (รายการสินค้า) {Lockguard Lock (m_mutex); m_container.pushback (รายการ); } รายการอ่าน () const {Lockguard lock (m_mutex); ส่งคืน m_container.first (); } `
Th Thielemann

มีหนึ่งกรณีการใช้งานที่ได้รับความนิยมอย่างมาก: จำนวนการอ้างอิง
Seva Alekseyev

33

มันมีประโยชน์ในสถานการณ์ที่คุณซ่อนสถานะภายในเช่นแคช ตัวอย่างเช่น:

ระดับ HashTable
{
...
ส่วนกลาง:
    การค้นหาสตริง (คีย์สตริง) const
    {
        if (key == lastKey)
            คืนค่าล่าสุด

        ค่าสตริง = lookupInternal (กุญแจ);

        lastKey = key;
        lastValue = ค่า;

        คืนค่า;
    }

เอกชน:
    สตริงที่ไม่แน่นอน lastKey, lastValue;
};

และจากนั้นคุณสามารถมีconst HashTableวัตถุที่ยังคงใช้lookup()วิธีการซึ่งปรับเปลี่ยนแคชภายใน


9

mutable มีอยู่เมื่อคุณอนุมานว่าจะอนุญาตให้ใครทำการแก้ไขข้อมูลในฟังก์ชันคงที่เป็นอย่างอื่น

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

คำหลักอาจทำหน้าที่เป็นคำแนะนำแก่คอมไพเลอร์ซึ่งผู้รวบรวมเชิงทฤษฎีสามารถวางวัตถุคงที่ (เช่นส่วนกลาง) ในหน่วยความจำที่ทำเครื่องหมายว่าอ่านได้อย่างเดียว การปรากฏตัวของmutableคำใบ้ว่าสิ่งนี้ไม่ควรทำ

นี่คือเหตุผลที่ถูกต้องในการประกาศและใช้ข้อมูลที่ไม่แน่นอน

  • ความปลอดภัยด้าย การประกาศ a mutable boost::mutexสมเหตุสมผลอย่างสมบูรณ์แบบ
  • สถิติ. การนับจำนวนการเรียกไปยังฟังก์ชันให้อาร์กิวเมนต์บางส่วนหรือทั้งหมด
  • memoization คำนวณคำตอบราคาแพงแล้วเก็บไว้เพื่ออ้างอิงในอนาคตแทนที่จะทำการคำนวณใหม่อีกครั้ง

2
คำตอบที่ดียกเว้นสำหรับความคิดเห็นที่ไม่แน่นอนว่าเป็น "คำใบ้" สิ่งนี้ทำให้ดูเหมือนว่าสมาชิกที่ไม่แน่นอนบางครั้งจะไม่สามารถเปลี่ยนแปลงได้หากคอมไพเลอร์วางวัตถุลงใน ROM พฤติกรรมของความไม่แน่นอนถูกกำหนดไว้อย่างดี
Richard Corden

2
นอกเหนือจากการวางวัตถุ const ในหน่วยความจำแบบอ่านอย่างเดียวคอมไพเลอร์อาจตัดสินใจที่จะเพิ่มประสิทธิภาพการเรียก fucntion const ออกจากลูปเช่น ตัวนับสถิติที่ไม่แน่นอนในฟังก์ชั่น const จะยังคงอนุญาตการปรับให้เหมาะสมดังกล่าว (และนับการโทรเพียงครั้งเดียว) แทนการป้องกันการปรับให้เหมาะสมเพื่อประโยชน์ในการนับการโทรมากขึ้น
Hagen von Eitzen

@HagenvonEitzen - ฉันค่อนข้างแน่ใจว่าไม่ถูกต้อง คอมไพเลอร์ไม่สามารถยกฟังก์ชันออกจากลูปได้เว้นแต่จะสามารถพิสูจน์ได้ว่าไม่มีผลข้างเคียง โดยทั่วไปแล้วข้อพิสูจน์นั้นเกี่ยวข้องกับการตรวจสอบการใช้งานของฟังก์ชั่น (บ่อยครั้งหลังจากที่มันถูก inline) และไม่พึ่งพาconst(และการตรวจสอบดังกล่าวจะประสบความสำเร็จหรือล้มเหลวโดยไม่คำนึงถึงconstหรือmutable) เพียงประกาศฟังก์ชั่นconstไม่เพียงพอ: constฟังก์ชั่นนั้นฟรีที่จะมีผลข้างเคียงเช่นการแก้ไขตัวแปรทั่วโลกหรือบางสิ่งที่ส่งผ่านไปยังฟังก์ชั่นดังนั้นมันจึงไม่ใช่การรับประกันที่มีประโยชน์สำหรับการพิสูจน์
BeeOnRope

ตอนนี้คอมไพเลอร์บางตัวมีส่วนขยายพิเศษเช่น_attribute __ ((const)) ของ gcc และ __attribute __ ((บริสุทธิ์)) ​​ซึ่ง _do มีเอฟเฟกต์ดังกล่าวแต่มีความเกี่ยวข้องกับconstคำสำคัญใน C ++ เท่านั้น
BeeOnRope

8

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

class CIniWrapper
{
public:
   CIniWrapper(LPCTSTR szIniFile);

   // non-const: logically modifies the state of the object
   void SetValue(LPCTSTR szName, LPCTSTR szValue);

   // const: does not logically change the object
   LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;

   // ...

private:
   // cache, avoids going to disk when a named value is retrieved multiple times
   // does not logically change the public interface, so declared mutable
   // so that it can be used by the const GetValue() method
   mutable std::map<string, string> m_mapNameToValue;
};

ตอนนี้คุณต้องใช้สิ่งนี้ด้วยความระมัดระวัง - ปัญหาที่เกิดขึ้นพร้อมกันนั้นเป็นเรื่องที่น่ากังวลอย่างมากเนื่องจากผู้โทรอาจคิดว่ามันปลอดภัยสำหรับเธรดหากใช้constเมธอดเท่านั้น และแน่นอนว่าการปรับเปลี่ยนmutableข้อมูลไม่ควรเปลี่ยนพฤติกรรมของวัตถุในรูปแบบที่สำคัญใด ๆ สิ่งที่อาจถูกละเมิดโดยตัวอย่างที่ฉันให้ไว้เช่นคาดว่าการเปลี่ยนแปลงที่เขียนไปยังดิสก์จะปรากฏในแอปทันที .


6

ไม่แน่นอนจะใช้เมื่อคุณมีตัวแปรภายในคลาสที่ใช้เฉพาะภายในคลาสนั้นเพื่อส่งสัญญาณสิ่งต่าง ๆ เช่น mutex หรือล็อค ตัวแปรนี้ไม่ได้เปลี่ยนพฤติกรรมของคลาส แต่มีความจำเป็นเพื่อใช้ความปลอดภัยของเธรดของคลาสเอง ดังนั้นหากไม่มี "ไม่แน่นอน" คุณจะไม่สามารถมีฟังก์ชั่น "const" เพราะตัวแปรนี้จะต้องมีการเปลี่ยนแปลงในทุกฟังก์ชั่นที่มีให้กับโลกภายนอก ดังนั้นความไม่แน่นอนถูกนำมาใช้เพื่อให้ตัวแปรสมาชิกเขียนได้แม้ฟังก์ชั่น const

ความไม่แน่นอนที่ระบุไว้แจ้งทั้งคอมไพเลอร์และผู้อ่านว่าปลอดภัยและคาดว่าตัวแปรสมาชิกอาจถูกแก้ไขภายในฟังก์ชันสมาชิก const


4

ความไม่แน่นอนส่วนใหญ่จะใช้ในรายละเอียดการใช้งานของชั้นเรียน ผู้ใช้ของชั้นไม่จำเป็นต้องรู้เกี่ยวกับมันดังนั้นวิธีการของเขาคิดว่า "ควร" เป็น const ได้ ตัวอย่างของการมี mutex สามารถเปลี่ยนแปลงได้เป็นตัวอย่างที่ดี


4

การใช้งานของคุณไม่ใช่แฮ็ค แต่เหมือนกับหลาย ๆ สิ่งใน C ++ ที่ไม่แน่นอนสามารถเป็นแฮ็คสำหรับโปรแกรมเมอร์ที่ขี้เกียจที่ไม่ต้องการย้อนกลับไปและทำเครื่องหมายบางสิ่งที่ไม่ควรเป็นแบบไม่ใช่ const


3

ใช้ "ไม่แน่นอน" สำหรับสิ่งที่ไร้สถานะอย่างมีเหตุผลต่อผู้ใช้ (และควรมี getters "const" ใน API สาธารณะระดับ ') แต่ไม่ไร้สถานะในการใช้งานพื้นฐาน (รหัสใน. cpp ของคุณ)

กรณีที่ฉันใช้บ่อยที่สุดคือการเริ่มต้นขี้เกียจของสมาชิก "ข้อมูลเก่า" ธรรมดา กล่าวคือเป็นกรณีที่เหมาะสมที่สุดเมื่อสมาชิกดังกล่าวมีราคาแพงในการสร้าง (ตัวประมวลผล) หรือพกพา (หน่วยความจำ) และผู้ใช้ของวัตถุจำนวนมากจะไม่ถามพวกเขา ในสถานการณ์เช่นนั้นคุณต้องการสร้างสันหลังยาวเพื่อประสิทธิภาพการทำงานเนื่องจาก 90% ของวัตถุที่สร้างขึ้นไม่จำเป็นต้องสร้างเลย แต่คุณยังต้องแสดง API ไร้สัญชาติที่ถูกต้องสำหรับการใช้งานสาธารณะ


2

เปลี่ยนแปลงไม่ได้ความหมายของconstบิตจาก const ในระดับบิตถึงตรรกะตรรกะสำหรับชั้นเรียน

ซึ่งหมายความว่าคลาสที่มีสมาชิกที่ไม่สามารถเปลี่ยนแปลงได้จะมีค่าบิตอยู่ในระดับบิตและจะไม่ปรากฏในส่วนที่อ่านได้อย่างเดียวของไฟล์ที่เรียกทำงานได้

นอกจากนี้ยังปรับเปลี่ยนประเภทการตรวจสอบโดยการอนุญาตให้สมาชิกฟังก์ชันการเปลี่ยนแปลงสมาชิกที่ไม่แน่นอนโดยไม่ต้องใช้constconst_cast

class Logical {
    mutable int var;

public:
    Logical(): var(0) {}
    void set(int x) const { var = x; }
};

class Bitwise {
    int var;

public:
    Bitwise(): var(0) {}
    void set(int x) const {
        const_cast<Bitwise*>(this)->var = x;
    }
};

const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.

int main(void)
{
    logical.set(5); // Well defined.
    bitwise.set(5); // Undefined.
}

ดูคำตอบอื่น ๆ สำหรับรายละเอียดเพิ่มเติม แต่ฉันต้องการเน้นว่าไม่ใช่เพียงเพื่อความปลอดภัยของประเภทและมันมีผลต่อผลลัพธ์ที่คอมไพล์แล้ว


1

ในบางกรณี (เช่นตัววนซ้ำที่ได้รับการออกแบบมาไม่ดี) ชั้นเรียนต้องเก็บจำนวนหรือค่าอื่น ๆ ที่ไม่ส่งผลกระทบต่อ "สถานะ" ที่สำคัญของชั้นเรียน นี่คือส่วนใหญ่ที่ฉันเห็นการใช้ไม่แน่นอน คุณจะถูกบังคับให้เสียสละผลงานออกแบบทั้งหมดของคุณ

รู้สึกเหมือนแฮ็คเป็นส่วนใหญ่เวลาฉันเช่นกัน มีประโยชน์ในสถานการณ์ที่น้อยมาก


1

ตัวอย่างคลาสสิก (ดังที่กล่าวไว้ในคำตอบอื่น ๆ ) และสถานการณ์เดียวที่ฉันได้เห็นmutableคำหลักที่ใช้ในขณะนี้คือการแคชผลของGetวิธีการที่ซับซ้อนซึ่งแคชจะดำเนินการเป็นสมาชิกข้อมูลของชั้นเรียนและไม่เป็น ตัวแปรคงที่ในวิธีการ (สำหรับเหตุผลของการแบ่งปันระหว่างฟังก์ชั่นหลาย ๆ หรือความสะอาดธรรมดา)

โดยทั่วไปทางเลือกในการใช้mutableคำหลักมักจะเป็นตัวแปรแบบคงที่ในวิธีการหรือconst_castเคล็ดลับ

อีกคำอธิบายรายละเอียดอยู่ในที่นี่


1
ฉันไม่เคยได้ยินการใช้สมาชิกแบบคงที่เป็นทางเลือกทั่วไปสำหรับสมาชิกที่ไม่แน่นอน และconst_castเป็นเพียงเมื่อคุณรู้ (หรือได้รับการรับรอง) ว่าบางสิ่งจะไม่เปลี่ยนแปลง (เช่นเมื่อรบกวนกับไลบรารี C) หรือเมื่อคุณรู้ว่ามันไม่ได้ประกาศ const นั่นคือการแก้ไขตัวแปร const-casted const ส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนด
เซบาสเตียนมัค

1
@phresnel โดย "ตัวแปรคงที่" ฉันหมายถึงตัวแปรอัตโนมัติคงที่ในวิธีการ (ที่อยู่ในสาย) และconst_castสามารถใช้ในการปรับเปลี่ยนสมาชิกชั้นเรียนในconstวิธีการซึ่งเป็นสิ่งที่ฉันอ้างถึง ...
Daniel Hershcovich

1
ที่ไม่ได้ล้างให้ฉันเป็นคุณเขียน "ทั่วไป" :) ที่เกี่ยวกับการปรับเปลี่ยนผ่านเป็นกล่าวนี้ได้รับอนุญาตเท่านั้นเมื่อวัตถุที่ไม่ได้ประกาศconst_cast constเช่นconst Frob f; f.something();กับvoid something() const { const_cast<int&>(m_foo) = 2;ผลลัพธ์ในพฤติกรรมที่ไม่ได้กำหนด
เซบาสเตียนมัค

1

ความไม่แน่นอนสามารถมีประโยชน์เมื่อคุณเอาชนะฟังก์ชันเสมือน const และต้องการแก้ไขตัวแปรสมาชิกคลาสลูกของคุณในฟังก์ชันนั้น ในกรณีส่วนใหญ่คุณไม่ต้องการเปลี่ยนอินเทอร์เฟซของคลาสพื้นฐานดังนั้นคุณต้องใช้ตัวแปรสมาชิกที่ไม่แน่นอนของคุณเอง


1

คำหลักที่ไม่แน่นอนมีประโยชน์มากเมื่อสร้างสตับเพื่อการทดสอบในชั้นเรียน คุณสามารถ stub ฟังก์ชั่น const และยังสามารถเพิ่มเคาน์เตอร์ (ไม่แน่นอน) หรือฟังก์ชั่นการทดสอบใด ๆ ที่คุณเพิ่มลงใน stub ของคุณ สิ่งนี้ทำให้อินเทอร์เฟซของคลาสที่ stubbed ยังคงอยู่


0

หนึ่งในตัวอย่างที่ดีที่สุดที่เราใช้ความไม่แน่นอนคือการทำสำเนาแบบลึก ในตัวสร้างสำเนาเราส่งconst &objเป็นอาร์กิวเมนต์ ดังนั้นวัตถุใหม่ที่สร้างขึ้นจะเป็นประเภทคงที่ ถ้าเราต้องการที่จะเปลี่ยนแปลง (ส่วนใหญ่เราจะไม่เปลี่ยนในกรณีที่หายากเราอาจเปลี่ยนแปลง) สมาชิกในวัตถุ const mutableนี้สร้างขึ้นใหม่ที่เราต้องการที่จะประกาศว่ามันเป็น

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

class Test
{
public:
    Test(): x(1), y(1) {};
    mutable int x;
    int y;
};

int main()
{
    const Test object;
    object.x = 123;
    //object.y = 123;
    /* 
    * The above line if uncommented, will create compilation error.
    */   

    cout<< "X:"<< object.x << ", Y:" << object.y;
    return 0;
}

Output:-
X:123, Y:1

ในตัวอย่างข้างต้นเราสามารถเปลี่ยนค่าของตัวแปรสมาชิกxแม้ว่าจะเป็นส่วนหนึ่งของวัตถุที่ถูกประกาศเป็น const นี่เป็นเพราะตัวแปรxถูกประกาศว่าไม่แน่นอน แต่ถ้าคุณพยายามแก้ไขค่าของตัวแปรสมาชิกyคอมไพเลอร์จะส่งข้อผิดพลาด


-1

คำหลักมาก 'ไม่แน่นอน' เป็นคำหลักที่สงวนไว้จริง ๆ แล้วมันถูกใช้เพื่อเปลี่ยนแปลงค่าของตัวแปรคงที่หากคุณต้องการมีค่าหลายค่าของ constsnt ให้ใช้คำหลักที่ไม่แน่นอน

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