"const" ใน C ++ มีการใช้งานอย่างไร


129

ในฐานะที่เป็นสามเณร c ++ Programmer constมีโครงสร้างบางอย่างที่มองยังคงเป็นอย่างปิดบังให้ฉันหนึ่งของเหล่านี้เป็น คุณสามารถใช้มันได้ในหลาย ๆ ที่และมีเอฟเฟกต์ต่าง ๆ มากมายซึ่งแทบจะเป็นไปไม่ได้เลยที่ผู้เริ่มต้นจะมีชีวิต กูรู C ++ บางคนจะอธิบายการใช้งานที่หลากหลายตลอดไปหรือไม่และ / หรือทำไมไม่ใช้?


กำลังมองหาคำถามนั้นอย่างแท้จริง: D
alamin

คำตอบ:


100

พยายามรวบรวมการใช้งานบางอย่าง:

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

ScopeGuard const& guard = MakeGuard(&cleanUpFunction);

คำอธิบายโดยใช้รหัส:

struct ScopeGuard { 
    ~ScopeGuard() { } // not virtual
};

template<typename T> struct Derived : ScopeGuard { 
    T t; 
    Derived(T t):t(t) { }
    ~Derived() {
        t(); // call function
    }
};

template<typename T> Derived<T> MakeGuard(T t) { return Derived<T>(t); }

เคล็ดลับนี้ใช้ในคลาสยูทิลิตี้ ScopeGuard ของ Alexandrescu เมื่อชั่วคราวออกไปนอกขอบเขตตัวทำลายของ Derived จะถูกเรียกอย่างถูกต้อง รหัสด้านบนขาดรายละเอียดเล็ก ๆ น้อย ๆ แต่นั่นเป็นเรื่องใหญ่สำหรับมัน


ใช้ const เพื่อบอกวิธีการอื่น ๆ จะไม่เปลี่ยนสถานะตรรกะของวัตถุนี้

struct SmartPtr {
    int getCopies() const { return mCopiesMade; }
};

ใช้ const สำหรับคลาส copy-on-writeเพื่อให้คอมไพเลอร์ช่วยให้คุณตัดสินใจได้ว่าคุณไม่จำเป็นต้องคัดลอกเมื่อใดและเมื่อใด

struct MyString {
    char * getData() { /* copy: caller might write */ return mData; }
    char const* getData() const { return mData; }
};

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

การใช้รหัส :

int main() {
    string const a = "1234";
    string const b = a;
    // outputs the same address for COW strings
    cout << (void*)&a[0] << ", " << (void*)&b[0];
}

ข้อมูลข้างต้นจะพิมพ์ที่อยู่เดียวกันใน GCC ของฉันเพราะที่ใช้ภาษา C ++ std::stringดำเนินห้องสมุดเขียนคัดลอกเมื่อ สตริงทั้งสองแม้ว่าจะเป็นอ็อบเจ็กต์ที่แตกต่างกัน แต่ก็ใช้หน่วยความจำเดียวกันสำหรับข้อมูลสตริง การสร้างbnon-const จะชอบเวอร์ชันที่ไม่ใช่ const operator[]และ GCC จะสร้างสำเนาของบัฟเฟอร์หน่วยความจำสำรองเนื่องจากเราสามารถเปลี่ยนแปลงได้และจะต้องไม่ส่งผลต่อข้อมูลของa!

int main() {
    string const a = "1234";
    string b = a;
    // outputs different addresses!
    cout << (void*)&a[0] << ", " << (void*)&b[0];
}

สำหรับตัวสร้างการคัดลอกเพื่อทำสำเนาจากวัตถุ const และจังหวะ :

struct MyClass {
    MyClass(MyClass const& that) { /* make copy of that */ }
};

สำหรับการสร้างค่าคงที่ที่ไม่สามารถเปลี่ยนแปลงได้เล็กน้อย

double const PI = 3.1415;

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

void PrintIt(Object const& obj) {
    // ...
}

2
คุณช่วยอธิบายการใช้งานครั้งแรกและครั้งที่สามในตัวอย่างของคุณได้ไหม
tunnuz

"สำหรับการรับประกัน callee ว่าพารามิเตอร์ต้องไม่เป็น NULL" ฉันไม่เห็นว่า const มีส่วนเกี่ยวข้องกับตัวอย่างนั้นอย่างไร
Logan Capaldo

โอ๊ะฉันล้มเหลว ฉันเริ่มเขียนเกี่ยวกับการอ้างอิง ขอบคุณมากสำหรับเสียงครวญคราง :) ฉันจะลบสิ่งนั้นออกตอนนี้ :)
Johannes Schaub - litb

3
กรุณาอธิบายตัวอย่างแรก ไม่สมเหตุสมผลกับฉันมากนัก
chikuba

28

มีการใช้งานหลัก 2 อย่างของ const ใน C ++

ค่าคงที่

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

void PrintStudent(const Student& student) {
  cout << student.GetName();
}

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

เห็นได้ชัดว่าการพิมพ์ข้อมูลไปยัง cout ไม่ต้องคิดมาก :)

ทำเครื่องหมายเมธอดสมาชิกเป็น const

ในตัวอย่างก่อนหน้านี้ฉันทำเครื่องหมายนักเรียนเป็น const แต่ C ++ รู้ได้อย่างไรว่าการเรียกเมธอด GetName () กับนักเรียนจะไม่ทำให้วัตถุกลายพันธุ์ คำตอบคือเมธอดถูกทำเครื่องหมายเป็น const

class Student {
  public:
    string GetName() const { ... }
};

การทำเครื่องหมาย method "const" ทำได้ 2 อย่าง โดยทั่วไปจะบอก C ++ ว่าวิธีนี้จะไม่กลายพันธุ์วัตถุของฉัน สิ่งที่สองคือตอนนี้ตัวแปรสมาชิกทั้งหมดจะได้รับการปฏิบัติราวกับว่าถูกทำเครื่องหมายเป็น const สิ่งนี้ช่วย แต่ไม่ได้ป้องกันไม่ให้คุณแก้ไขอินสแตนซ์ของชั้นเรียนของคุณ

นี่เป็นตัวอย่างที่ง่ายมาก แต่หวังว่าจะช่วยตอบคำถามของคุณได้


16

โปรดทำความเข้าใจความแตกต่างระหว่างการประกาศทั้ง 4 นี้:

การประกาศ 2 รายการต่อไปนี้มีความหมายเหมือนกัน คุณเปลี่ยนตำแหน่งที่ ccp1 และ ccp2 ชี้ได้ แต่ไม่สามารถเปลี่ยนสิ่งที่ชี้ไปได้

const char* ccp1;
char const* ccp2;

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

char* const cpc = &something_possibly_not_const;

สุดท้ายเรารวมทั้งสองอย่างเข้าด้วยกันดังนั้นสิ่งที่ชี้ไปนั้นไม่สามารถแก้ไขได้และตัวชี้ไม่สามารถชี้ไปที่อื่นได้

const char* const ccpc = &const_obj;

กฎเกลียวตามเข็มนาฬิกาสามารถช่วยแก้ปัญหาการประกาศhttp://c-faq.com/decl/spiral.anderson.html


ในทางอ้อมใช่แล้ว! กฎเกลียวตามเข็มนาฬิกาอธิบายได้ดีกว่า - เริ่มที่ชื่อ (kpPointer) และวาดเกลียวตามเข็มนาฬิกาผ่านโทเค็นและพูดแต่ละโทเค็น เห็นได้ชัดว่าไม่มีอะไรทางด้านขวาของ kpPointer แต่ยังใช้งานได้
Steve Folly

3

ดังที่ได้อ่านมาที่นี่การสังเกตว่ามีประโยชน์

const ใช้กับสิ่งที่อยู่ทางซ้ายทันที (นอกเหนือจากกรณีที่ไม่มีอะไรในกรณีนี้จะใช้กับสิ่งใดก็ตามที่อยู่ทางขวาทันที)

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