Giải thích sử dụng Const trong C ++


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

Ai đó có thể giải thích cách sử dụng của mỗi const?


27
Tôi thực sự thích phương pháp này để giải mã các khai báo phức tạp: c-faq.com/decl/spiral.anderson.html
Jason

Câu trả lời:


77

Đọc phần này: https://isocpp.org/wiki/faq/const-correctness

Cuối cùng constcó nghĩa là hàm Method3không sửa đổi các thành viên không thể thay đổi của lớp nó.

const int* constcó nghĩa là một con trỏ không đổi đến một hằng số int: tức là một con trỏ không thể thay đổi, đến một int không thể thay đổi: sự khác biệt duy nhất giữa điều này và const int&là nó có thể đượcnull

const int* const&có nghĩa là một tham chiếu đến một con trỏ hằng đến một hằng số int. Thông thường các con trỏ không được chuyển qua tham chiếu; const int* &có ý nghĩa hơn vì nó có nghĩa là con trỏ có thể được thay đổi trong khi gọi phương thức, đó sẽ là lý do duy nhất tôi có thể thấy để chuyển một con trỏ bằng tham chiếu, const int* const&là tất cả các ý định và mục đích giống nhau const int* constngoại trừ việc nó có thể kém hiệu quả hơn vì con trỏ là các loại dữ liệu cũ thuần túy (POD) và chúng nói chung phải được chuyển theo giá trị.


103

Sẽ dễ hiểu hơn nếu bạn viết lại nó dưới dạng hoàn toàn tương đương

// 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;

sau đó đọc nó từ phải sang trái.

# 5 nói rằng toàn bộ khai báo hàm ở bên trái const, ngụ ý rằng đây nhất thiết phải là một hàm thành viên hơn là một hàm miễn phí.

# 4 nói rằng con trỏ bên trái là const(có thể không được thay đổi để trỏ đến một địa chỉ khác).

# 3 nói rằng intbên trái là const(có thể không được thay đổi để có giá trị khác).

# 2 nói rằng con trỏ bên trái là const.

# 1 nói rằng intbên trái là const.

Tổng hợp tất cả lại với nhau, bạn có thể đọc đây là một consthàm thành viên có tên có tên Method3tham chiếu đến một constcon trỏ đến một int const(hoặc a const int, nếu bạn thích) và trả về một constcon trỏ tới một int const( const int).

(Nb # 2 hoàn toàn không cần thiết .)


22

Trước hết const Tlà tương đương với T const.

const int* constdo đó tương đương với int const * const.

Khi đọc các biểu thức có nhiều constmã thông báo và con trỏ trong đó, hãy luôn cố gắng đọc chúng từ phải sang trái (sau khi áp dụng phép biến đổi ở trên). Vì vậy, trong trường hợp này giá trị trả về là một con trỏ const tới một constint . Bản thân con trỏ constkhông có ý nghĩa gì ở đây vì giá trị trả về không phải là một giá trị có thể được sửa đổi. constTuy nhiên, việc làm cho pointee đảm bảo rằng người gọi không thể sửa đổi int(hoặc mảng các int) được trả về bởi Method3.

const int*const&trở thành int const*const&, vì vậy nó là một tham chiếu đến một con trỏ const tới một constint . Truyền một con trỏ const bởi tham chiếu nam cũng không có ý nghĩa gì - bạn không thể sửa đổi giá trị được tham chiếu vì con trỏ là constvà các tham chiếu và con trỏ chiếm dung lượng như nhau nên cũng không tiết kiệm được dung lượng.

Cuối cùng constchỉ ra rằng phương thức không sửa đổi thisđối tượng. Con thistrỏ trong phần thân phương thức sẽ có phần khai báo (lý thuyết) T const * const this. Điều này có nghĩa là một const T*đối tượng sẽ có thể gọi T::Method3().


2
Bỏ phiếu cho câu trả lời này (và câu trả lời tương tự của ildjarn), một phần là để đưa ra quan điểm rằng toàn bộ điều có ý nghĩa hơn nếu bạn không đặt các chữ cái đầu tiên constở đầu cụm từ. Đây chính là lý do tại sao tôi nghĩ rằng việc đặt nó ở đó là một việc làm constkhông tốt, mặc dù ngôn ngữ cho phép nó và đó là cách sử dụng phổ biến nhất.
TED

12

Một cách dễ dàng để nhớ các quy tắc constlà suy nghĩ về nó theo cách này: constáp dụng cho thứ ở bên trái của nó, trừ khi không có thứ gì ở bên trái của nó.

Vì vậy, trong trường hợp const int * const, hằng số đầu tiên không có gì ở bên trái của nó, vì vậy nó áp dụng cho intvà hằng số thứ hai không có gì đó ở bên trái, vì vậy nó áp dụng cho con trỏ.

Quy tắc này cũng cho bạn biết điều gì sẽ xảy ra trong trường hợp bạn có const int const *. Vì cả hai const áp dụng cho intbiểu thức này là thừa và do đó không hợp lệ.


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

Tôi thích sử dụng phương pháp "đồng hồ" hoặc "xoắn ốc" trong đó bắt đầu từ tên mã định danh (trong trường hợp này Method3) bạn đọc qua lại từ trái sang phải-qua lại-trái, v.v. để giải mã quy ước đặt tên. Vì vậy, const int* const Method3(const int* const&) constmột phương thức lớp không thay đổi bất kỳ thành viên lớp nào (của một số lớp chưa được đặt tên) và nhận tham chiếu không đổi đến một con trỏ trỏ đến một hằng số intvà trả về một con trỏ hằng số thành một hằng số int.

Hi vọng điêu nay co ich,

Jason


2

Một cách dễ dàng để nhớ const trong C ++ là khi bạn nhìn thấy một số mã ở dạng như:

XXX const;
const YYY;

XXX, YYY sẽ là một thành phần không đổi,
XXX constdạng:

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

const YYY hình thức:

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

Mọi người thường sử dụng những loại này. Khi bạn nhìn thấy "const&"một nơi nào đó, đừng cảm thấy bối rối, const đang mô tả một cái gì đó trước chính nó. vì vậy câu trả lời của vấn đề này là hiển nhiên bây giờ.

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

2

Tôi chỉ muốn đề cập rằng đó const int* const&thực sự là một tham chiếu liên tục const int*. Ví dụ:

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'

Nó cũng là trường hợp của int* const&, Có nghĩa là: "Tham chiếu liên tục đến int*".
Nhưng const int*&là một tham chiếu không liên tục đến const int*.
Hi vọng điêu nay co ich.


1

Đọc từ phải sang trái giúp hiểu các bổ ngữ dễ dàng hơn.

Một phương thức const nhận tham chiếu đến một con trỏ const tới một const int được gọi Method3để trả về một con trỏ const tới một const int.

  1. Một phương thức const không thể sửa đổi các thành viên (trừ khi chúng rõ ràng mutable)
  2. Một con trỏ const không thể được thay đổi để trỏ đến một cái gì đó khác
  3. Không thể sửa đổi const int (hoặc bất kỳ kiểu nào khác)

1

const # 1: Con trỏ do Method3 trả về tham chiếu đến một int const.

const # 2: Giá trị con trỏ được trả về bởi chính hàm, là const. Đây là một hằng số vô dụng (mặc dù có giá trị về mặt vật lý), vì giá trị trả về từ một hàm không thể là giá trị l.

const # 3: Kiểu con trỏ được truyền bởi tham chiếu đến hàm trỏ đến một const int.

const # 4: Giá trị con trỏ được truyền bởi tham chiếu đến hàm, chính nó là một con trỏ const. Khai báo một giá trị được truyền cho một hàm như const thường sẽ là vô nghĩa, nhưng giá trị này được truyền bằng tham chiếu, vì vậy nó có thể có ý nghĩa.

const # 5: Hàm (có lẽ là một hàm thành viên) là const, có nghĩa là nó không được phép (a) gán giá trị mới cho bất kỳ thành viên nào của đối tượng mà nó là một phần hoặc (b) gọi một hàm thành viên không phải const trên đối tượng hoặc bất kỳ thành viên nào của nó.


0
  • const ở cuối phương thức là ký hiệu định tính cho biết trạng thái của đối tượng sẽ không bị thay đổi.

  • const int*const&biểu thị việc nhận bằng cách tham chiếu một con trỏ const đến một vị trí const. Nó không thể thay đổi để trỏ đến một vị trí khác hoặc thay đổi giá trị mà nó đang trỏ tới.

  • const int*const là giá trị trả về cũng là một con trỏ không đổi đến một vị trí không đổi.


0

Một vài ví dụ có thể hay để chứng minh khái niệm này, càng nhiều thì càng tốt.

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;
   }
};

Tôi hi vọng cái này giúp được!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.