คำอธิบายการใช้งาน C ++ Const


99
const int* const Method3(const int* const&) const;

ใครช่วยอธิบายการใช้งานของ const แต่ละตัวได้ไหม


27
ฉันชอบวิธีนี้มากในการถอดรหัสการประกาศที่ซับซ้อน: c-faq.com/decl/spiral.anderson.html
Jason

คำตอบ:


77

อ่านสิ่งนี้: https://isocpp.org/wiki/faq/const-correctness

ขั้นสุดท้ายconstหมายความว่าฟังก์ชันMethod3ไม่แก้ไขสมาชิกที่ไม่เปลี่ยนแปลงของคลาส

const int* constหมายถึงตัวชี้ค่าคงที่ไปยังค่าคงที่ int: คือตัวชี้ที่ไม่สามารถเปลี่ยนเป็น int ที่ไม่สามารถเปลี่ยนแปลงได้: ความแตกต่างเพียงอย่างเดียวระหว่างสิ่งนี้กับconst int&มันสามารถเป็นได้null

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


104

มันง่ายกว่าที่จะเข้าใจถ้าคุณเขียนใหม่ให้เทียบเท่ากันทั้งหมด

// v───v───v───v───v───v───v───v───v───v───v───v─┬┐
//                                               ││
//  v──#1    v─#2             v──#3    v─#4      #5
   int const * const Method3(int const * const&) const;

จากนั้นอ่านจากขวาไปซ้าย

# 5 กล่าวว่าการประกาศฟังก์ชันทั้งหมดทางด้านซ้ายคือconstซึ่งหมายความว่าสิ่งนี้จำเป็นต้องเป็นฟังก์ชันสมาชิกแทนที่จะเป็นฟังก์ชันอิสระ

# 4 บอกว่าตัวชี้ทางด้านซ้ายคือconst(อาจไม่สามารถเปลี่ยนให้ชี้ไปยังที่อยู่อื่นได้)

# 3 บอกว่าintทางซ้ายคือconst(ไม่สามารถเปลี่ยนเป็นค่าอื่นได้)

# 2 บอกว่าตัวชี้ทางซ้ายคือconst.

# 1 บอกว่าintทางซ้ายคือconst.

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

(Nb # 2 ไม่จำเป็นเลย)


22

ประการแรกconst Tเทียบเท่ากับT const.

const int* constint const * constจึงเทียบเท่ากับ

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

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

สุดท้ายconstบ่งชี้ว่าเมธอดไม่ได้แก้ไขthisวัตถุ thisชี้ภายในร่างกายวิธีการที่จะมี (ทฤษฎี) T const * const thisประกาศ ซึ่งหมายความว่าconst T*วัตถุจะสามารถโทรT::Method3()ได้


2
การโหวตสิ่งนี้ (และคำตอบที่คล้ายกันของ ildjarn) เป็นส่วนหนึ่งในการชี้ให้เห็นว่าสิ่งทั้งหมดมีความหมายมากขึ้นหากคุณไม่ใส่consts แรกไว้ที่หัวของวลี นี่เป็นเหตุผลว่าทำไมฉันถึงคิดว่าเป็นการปฏิบัติที่ไม่ดีconstแม้ว่าภาษาจะอนุญาตและเป็นการใช้งานที่พบบ่อยที่สุด
TED

12

วิธีง่ายๆในการจำกฎconstคือการคิดวิธีนี้: constใช้กับสิ่งที่อยู่ทางซ้ายเว้นแต่จะไม่มีอะไรอยู่ทางซ้าย

ดังนั้นในกรณีของconst int * constconst ตัวแรกจะไม่มีอะไรอยู่ทางซ้ายดังนั้นจึงใช้กับintและอันที่สองมีบางอย่างทางซ้ายดังนั้นจึงใช้กับตัวชี้

const int const *กฎนี้ยังบอกคุณในสิ่งที่จะเกิดขึ้นในกรณีที่คุณมี เนื่องจาก const ทั้งสองใช้กับintนิพจน์นี้ซ้ำซ้อนจึงไม่ถูกต้อง


3
const /* don't modify the int or array of ints' value(s) */
int* const /* as a retval, ignored. useless declaration */
Method3(const /* don't modify the int or array of ints' value(s) */
int* const /* don't modify the pointer's value, the address to which `pointer` points to. e.g. you cannot say `++pointer` */
&) const; /* this method does not modify the instance/object which implements the method */

3

ฉันชอบใช้วิธีการ"นาฬิกา" หรือ "เกลียว"โดยเริ่มจากชื่อตัวระบุ (ในกรณีนี้Method3) คุณอ่านกลับไปกลับมาจากซ้ายไปขวา - กลับไปซ้าย ฯลฯ เพื่อถอดรหัส หลักการตั้งชื่อ ดังนั้นconst int* const Method3(const int* const&) constเป็นวิธีการเรียนที่ไม่เปลี่ยนแปลงสมาชิกชั้นใด ๆ (บางระดับยกเลิกการตั้งชื่อ) และใช้อ้างอิงอย่างต่อเนื่องที่จะเป็นตัวชี้ว่าจุดที่จะต้องคงที่และผลตอบแทนที่เป็นตัวชี้คงที่คงที่intint

หวังว่านี่จะช่วยได้

เจสัน


2

วิธีง่ายๆในการจำ const ใน C ++ คือเมื่อคุณเห็นรหัสในรูปแบบเช่น:

XXX const;
const YYY;

XXX, YYY จะเป็นส่วนประกอบคงที่
XXX constรูปแบบ:

function ( def var ) const;    ------#1
* const;                       ------#2

const YYY แบบฟอร์ม:

const int;                     ------#3
const double;

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

const int* const Method3(const int* const&) const;
  |          |             |          |       |
  #3         #2            #3         #2      #1

2

ฉันเพียงต้องการที่จะพูดถึงว่าเป็นจริงอย่างต่อเนื่องเพื่อการอ้างอิงconst int* const& const int*ตัวอย่างเช่น:

int i = 0;
int j = 1;
int* p = &i;
int* q = &j;
const int* const& cpref = p;
cpref = q; //Error: assignment of read-only reference 'cpref'

นอกจากนี้ยังเป็นกรณีสำหรับint* const&ซึ่งหมายความว่า: "การอ้างอิงคงที่int*"
แต่คือการอ้างอิงที่ไม่คงที่const int*& หวังว่านี่จะช่วยได้ const int*


1

การอ่านจากขวาไปซ้ายทำให้เข้าใจตัวปรับแต่งได้ง่ายขึ้น

วิธีการ const ที่ใช้การอ้างอิงตัวชี้ const ไปยัง const int ที่เรียกว่าMethod3ซึ่งจะส่งกลับตัวชี้ const เป็น const int

  1. วิธีการ const ไม่สามารถแก้ไขสมาชิกได้ (เว้นแต่ว่าจะถูกเปิดเผย mutable )
  2. ไม่สามารถเปลี่ยนตัวชี้ const ให้ชี้ไปที่อย่างอื่นได้
  3. ไม่สามารถแก้ไข const int (หรือประเภทอื่น ๆ ) ได้

1

const # 1: ตัวชี้ที่ส่งคืนโดย Method3 หมายถึง const int

const # 2: ค่าตัวชี้ที่ส่งกลับโดยฟังก์ชันคือค่า const นี่คือ const ที่ไร้ประโยชน์ (แม้ว่าจะถูกต้องตามหลักไวยากรณ์) เนื่องจากค่าที่ส่งกลับจากฟังก์ชันไม่สามารถเป็นค่า l ได้

const # 3: ประเภทตัวชี้ที่ส่งผ่านโดยการอ้างอิงไปยังฟังก์ชันชี้ไปที่ const int

const # 4: ค่าตัวชี้ที่ส่งผ่านโดยการอ้างอิงถึงฟังก์ชันคือตัวชี้ const การประกาศค่าที่ส่งผ่านไปยังฟังก์ชันเป็น const โดยปกติจะไม่มีจุดหมาย แต่ค่านี้ถูกส่งผ่านโดยการอ้างอิงดังนั้นจึงมีความหมายได้

const # 5: ฟังก์ชัน (น่าจะเป็นฟังก์ชันสมาชิก) คือ const ซึ่งหมายความว่าไม่อนุญาตให้ (a) กำหนดค่าใหม่ให้กับสมาชิกใด ๆ ของวัตถุที่เป็นส่วนหนึ่งหรือ (b) เรียกฟังก์ชันที่ไม่ใช่สมาชิก const บนวัตถุหรือสมาชิกใด ๆ


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

  • const int*const&หมายถึงการรับโดยการอ้างอิงตัวชี้ const ไปยังตำแหน่ง const ไม่สามารถเปลี่ยนเพื่อชี้ไปยังตำแหน่งอื่นหรือเปลี่ยนค่าที่ชี้ไป

  • const int*const คือค่าส่งคืนซึ่งเป็นตัวชี้ค่าคงที่ไปยังตำแหน่งคงที่


0

ตัวอย่างบางส่วนอาจแสดงให้เห็นถึงแนวคิดนี้ได้ดียิ่ง imho ยิ่งดี

class TestClass
{
private:
   int iValue;
   int* oValuePtr;
   int& oValueRef;

public:
   int TestClass::ByValMethod1(int Value)
   {
      // Value can be modified
      Value++;

      // iValue can be modified
      iValue = Value;
      iValue += 1;

      // Return value can be modified
      return ++iValue;
   }

   int TestClass::ByValMethod2(const int Value)
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // iValue can be modified
      iValue = Value;
      iValue += 1;

      // Return value can be modified
      return ++iValue;
   }

   const int TestClass::ByValMethod3(int Value)
   {
      // Value can be modified
      Value++;

      // iValue can be modified
      iValue = Value;
      iValue += 1;

      // Return value can be modified
      return ++iValue;
   }

   const int TestClass::ByValMethod4(const int Value)
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // iValue can be modified
      iValue = Value;
      iValue += 1;

      // Return value can be modified
      return ++iValue;
   }

   const int TestClass::ByValMethod5(const int Value) const
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // iValue *cannot* be modified
      // Access through a const object
      iValue = Value;
      iValue += 1;

      // Return value *cannot* be modified
      // Access through a const object
      return ++iValue;
   }

   int& TestClass::ByRefMethod1(int& Value)
   {
      // Value can be modified
      Value++;

      // oValueRef can be modified
      oValueRef = Value;
      oValueRef += 1;

      // Return value can be modified
      return ++oValueRef;
   }

   int& TestClass::ByRefMethod2(const int& Value)
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // oValueRef can be modified
      oValueRef = Value;
      oValueRef += 1;

      // Return value can be modified
      return ++oValueRef;
   }

   const int& TestClass::ByRefMethod3(int& Value)
   {
      // Value can be modified
      Value++;

      // oValueRef can be modified
      oValueRef = Value;
      oValueRef += 1;

      // Return value can be modified
      return ++oValueRef;
   }

   const int& TestClass::ByRefMethod4(const int& Value)
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // oValueRef can be modified
      oValueRef = Value;
      oValueRef += 1;

      // Return value can be modified
      return ++oValueRef;
   }

   const int& TestClass::ByRefMethod5(const int& Value) const
   {
      // Value *cannot* be modified
      // Variable is const variable
      Value++;

      // oValueRef can be modified
      oValueRef = Value;
      oValueRef += 1;

      // Return value can be modified
      return ++oValueRef;
   }

   int* TestClass::PointerMethod1(int* Value)
   {
      // Value can be modified
      Value++;

      // oValuePtr can be assigned
      oValuePtr = Value;

      // oValuePtr can be modified
      oValuePtr += 1;

      // Return value can be modified
      return ++oValuePtr;
   }

   int* TestClass::PointerMethod2(const int* Value)
   {
      // Value can be modified
      Value++;

      // oValuePtr cannot be assigned
      // const int* to int*
      oValuePtr = Value;

      // oValuePtr can be modified
      oValuePtr += 1;

      // Return value can be modified
      return ++oValuePtr;
   }

   const int* TestClass::PointerMethod3(int* Value)
   {
      // Value can be modified
      Value++;

      // oValuePtr can be assigned
      oValuePtr = Value;

      // iValue can be modified
      oValuePtr += 1;

      // Return value can be modified
      return ++oValuePtr;
   }

   const int* TestClass::PointerMethod4(const int* Value)
   {
      // Value cannot be modified
      Value++;

      // oValuePtr *cannot* be assigned
      // const int* to int*
      oValuePtr = Value;

      // oValuePtr can be modified
      oValuePtr += 1;

      // Return value can be modified
      return ++oValuePtr;
   }

   const int* TestClass::PointerMethod5(const int* Value) const
   {
      // Value can be modified
      ++Value;

      // oValuePtr *cannot* be assigned
      // const int* to int* const
      // Access through a const object
      oValuePtr = Value;

      // oValuePtr *cannot* be modified
      // Access through a const object
      oValuePtr += 1;

      // Return value *cannot* be modified
      return ++oValuePtr;
   }
};

ฉันหวังว่านี่จะช่วยได้!

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