Sử dụng 'const' cho các tham số chức năng


397

Làm thế nào đến nay bạn có đi với const? Bạn chỉ thực hiện các chức năng constkhi cần thiết hoặc bạn đi toàn bộ con lợn và sử dụng nó ở khắp mọi nơi? Ví dụ, hãy tưởng tượng một trình biến đổi đơn giản có một tham số boolean duy nhất:

void SetValue(const bool b) { my_val_ = b; }

Điều đó có constthực sự hữu ích không? Cá nhân tôi chọn sử dụng rộng rãi, bao gồm các tham số, nhưng trong trường hợp này tôi tự hỏi liệu nó có đáng không?

Tôi cũng ngạc nhiên khi biết rằng bạn có thể bỏ qua constcác tham số trong khai báo hàm nhưng có thể đưa nó vào định nghĩa hàm, ví dụ:

tập tin .h

void func(int n, long l);

tập tin .cpp

void func(const int n, const long l)

Có một lý do cho điều này? Nó có vẻ hơi bất thường với tôi.


Tôi không đồng ý. Tệp .h cũng phải có định nghĩa const. Nếu không, thì nếu tham số const được truyền cho hàm, trình biên dịch sẽ tạo ra lỗi, vì nguyên mẫu trong tệp .h không có định nghĩa const.
selwyn

10
Tôi đồng ý. :-) (Với câu hỏi, không phải bình luận cuối cùng!) Nếu không thay đổi giá trị trong phần thân của hàm, điều này có thể giúp dừng ngớ ngẩn == hoặc = lỗi, bạn không bao giờ nên đặt const trong cả hai, (nếu nó được thông qua bởi giá trị, bạn phải khác) Nó không đủ nghiêm trọng để tranh luận về nó mặc dù!
Chris Huang-Leaver

19
@selwyn: Tuy nhiên, ngay cả khi bạn truyền const int cho hàm, nó sẽ bị sao chép (vì nó không phải là tài liệu tham khảo), và vì vậy, const-ness không thành vấn đề.
jalf

1
Cuộc tranh luận tương tự xảy ra trong câu hỏi này: stackoverflow.com/questions/1554750/ cấp
Một phần

5
Tôi nhận ra bài đăng này là một vài năm tuổi, nhưng là một lập trình viên mới, tôi đã tự hỏi chính câu hỏi này và tôi đã vấp phải cuộc trò chuyện này. Theo tôi, nếu một hàm không nên thay đổi một giá trị, cho dù đó là tham chiếu hay bản sao của giá trị / đối tượng, thì nó phải là const. Nó an toàn hơn, nó tự ghi lại và nó thân thiện hơn với gỡ lỗi. Ngay cả đối với hàm đơn giản nhất, có một câu lệnh, tôi vẫn sử dụng const.

Câu trả lời:


187

Lý do là const cho tham số chỉ áp dụng cục bộ trong hàm, vì nó đang làm việc trên một bản sao của dữ liệu. Điều này có nghĩa là chữ ký hàm thực sự giống nhau. Có lẽ phong cách xấu để làm điều này rất nhiều mặc dù.

Cá nhân tôi có xu hướng không sử dụng const ngoại trừ các tham số con trỏ và tham chiếu. Đối với các đối tượng được sao chép, điều đó không thực sự quan trọng, mặc dù nó có thể an toàn hơn vì nó báo hiệu ý định trong chức năng. Đó thực sự là một cuộc gọi phán xét. Tôi có xu hướng sử dụng const_iterator mặc dù khi lặp lại một cái gì đó và tôi không có ý định sửa đổi nó, vì vậy tôi đoán theo cách riêng của mình, miễn là tính chính xác của các kiểu tham chiếu được duy trì nghiêm ngặt.


57
Tôi không thể đồng ý với phần 'phong cách xấu'. Thả consttừ các nguyên mẫu hàm có lợi thế là bạn không cần thay đổi tệp tiêu đề nếu bạn quyết định bỏ consttừ phần triển khai sau.
Michał Górny

4
"Cá nhân tôi có xu hướng không sử dụng const trừ các tham số con trỏ và tham chiếu." Có lẽ bạn nên làm rõ rằng "Tôi có xu hướng không sử dụng vòng loại không cần thiết trong khai báo hàm, nhưng sử dụng constở nơi nó tạo ra sự khác biệt hữu ích."
Ded repeatator

3
Tôi không đồng ý với câu trả lời này. Tôi nghiêng theo cách khác và đánh dấu các tham số constbất cứ khi nào có thể; nó biểu cảm hơn. Khi tôi đọc mã của người khác, tôi sử dụng các chỉ số nhỏ như thế này để đánh giá mức độ quan tâm của họ khi viết mã của họ bên cạnh những thứ như số ma thuật, nhận xét và sử dụng con trỏ thích hợp, v.v.
Ultimater

4
int getDouble(int a){ ++a; return 2*a; }Thử cái này. Tất nhiên, ++akhông có gì để làm ở đó nhưng nó có thể được tìm thấy ở đó trong một hàm dài được viết bởi nhiều hơn một lập trình viên trong một khoảng thời gian dài. Tôi mạnh mẽ đề nghị viết int getDouble( const int a ){ //... }rằng sẽ tạo ra một lỗi biên dịch khi tìm kiếm ++a;.
dom_beau

3
Tất cả là vấn đề ai cần thông tin nào. Bạn cung cấp tham số theo giá trị để người gọi không cần biết bất cứ điều gì về những gì bạn làm (nội bộ) với nó. Vì vậy, viết class Foo { int multiply(int a, int b) const; }trong tiêu đề của bạn. Trong triển khai của bạn, bạn quan tâm rằng bạn có thể hứa sẽ không thay đổi abvì vậy int Foo::multiply(const int a, const int b) const { }có ý nghĩa ở đây. (Sidenote: cả người gọi và người thực hiện đều quan tâm đến thực tế là hàm không làm thay đổi Foođối tượng của nó , do đó, const ở cuối phần khai báo)
CharonX

415

"const là vô nghĩa khi đối số được truyền theo giá trị vì bạn sẽ không sửa đổi đối tượng của người gọi."

Sai lầm.

Đó là về việc tự ghi lại mã của bạn và các giả định của bạn.

Nếu mã của bạn có nhiều người làm việc với nó và các chức năng của bạn không tầm thường thì bạn nên đánh dấu "const" bất kỳ và mọi thứ bạn có thể. Khi viết mã sức mạnh công nghiệp, bạn phải luôn cho rằng đồng nghiệp của mình là những kẻ thái nhân cách cố gắng giúp bạn có được bất cứ cách nào họ có thể (đặc biệt là vì nó thường là chính bạn trong tương lai).

Ngoài ra, như ai đó đã đề cập trước đó, nó có thể giúp trình biên dịch tối ưu hóa mọi thứ một chút (mặc dù đó là một cú sút xa).


41
Hoàn toàn đồng ý. Đó là tất cả về giao tiếp với mọi người và hạn chế những gì có thể được thực hiện với một biến với những gì nên làm.
Len Holgate

19
Tôi đã bình chọn cái này xuống. Tôi nghĩ rằng bạn pha loãng những gì bạn đang cố gắng chỉ ra với const khi bạn áp dụng nó cho các đối số giá trị đơn giản.
tonylo

26
Tôi đã bình chọn cái này lên. Khai báo tham số 'const' thêm thông tin ngữ nghĩa vào tham số. Họ nêu bật những gì tác giả ban đầu của mã đã dự định và điều này sẽ giúp duy trì mã khi thời gian trôi qua.
Richard Corden

13
@tonylo: bạn hiểu lầm rồi. Đây là về việc đánh dấu một biến cục bộ là const bên trong một khối mã (đó là một hàm). Tôi sẽ làm tương tự cho bất kỳ biến địa phương. Nó là trực giao để có một API là chính xác, điều này thực sự cũng quan trọng.
rlerallut

56
Và nó có thể bắt lỗi bên trong hàm - nếu bạn biết rằng một tham số không nên thay đổi, thì việc khai báo nó là const có nghĩa là trình biên dịch sẽ cho bạn biết nếu bạn vô tình sửa đổi nó.
Adrian

156

Đôi khi (quá thường xuyên!) Tôi phải gỡ rối mã C ++ của người khác. Và tất cả chúng ta đều biết rằng mã C ++ của người khác là một mớ hỗn độn gần như theo định nghĩa :) Vì vậy, điều đầu tiên tôi làm để giải mã luồng dữ liệu cục bộ là đặt const trong mọi định nghĩa biến cho đến khi trình biên dịch bắt đầu sủa. Điều này có nghĩa là các đối số giá trị đủ điều kiện là tốt, bởi vì chúng chỉ là các biến cục bộ ưa thích được khởi tạo bởi người gọi.

À, tôi ước các biến được const theo mặc định và có thể thay đổi được cho các biến không const :)


4
"Tôi muốn các biến được mặc định là const" - một oxymoron ?? 8-) Nghiêm túc mà nói, "cấu thành" mọi thứ giúp bạn gỡ rối mã như thế nào? Nếu người viết ban đầu đã thay đổi một đối số được cho là hằng số, làm thế nào để bạn biết rằng var được coi là một hằng số? Hơn nữa, phần lớn các biến (không đối số) có nghĩa là ... biến. Vì vậy, trình biên dịch sẽ phá vỡ rất sớm sau khi bạn bắt đầu quá trình, không?
ysap

8
@ysap, 1. Đánh dấu const càng nhiều càng tốt cho phép tôi xem phần nào đang di chuyển và phần nào không. Theo kinh nghiệm của tôi, nhiều người dân địa phương là thực tế, không phải là cách khác. 2. "Biến Const" / "Biến bất biến" có thể nghe là oxymoron, nhưng là thông lệ tiêu chuẩn trong các ngôn ngữ chức năng, cũng như một số ngôn ngữ không chức năng; xem Rust chẳng hạn: doc.rust-lang.org/book/variable-bindings.html
Constantin

1
Ngoài ra tiêu chuẩn bây giờ trong một số trường hợp trong c ++; ví dụ, lambda [x](){return ++x;}là một lỗi; xem tại đây
anatolyg

10
Các biến là " const" theo mặc định trong Rust :)
phoenix

Các biến không nhất thiết phải được gán để cho phép chúng thay đổi; giá trị chúng được khởi tạo cũng có thể thay đổi khi chạy.
Sphynx

80

Hai dòng sau tương đương về chức năng:

int foo (int a);
int foo (const int a);

Rõ ràng là bạn sẽ không thể sửa đổi atrong cơ thể foonếu nó được xác định theo cách thứ hai, nhưng không có sự khác biệt so với bên ngoài.

Trường hợp constthực sự có ích là với các tham số con trỏ hoặc tham chiếu:

int foo (const BigStruct &a);
int foo (const BigStruct *a);

Điều này nói rằng foo có thể có một tham số lớn, có lẽ là cấu trúc dữ liệu có kích thước gigabyte, mà không cần sao chép nó. Ngoài ra, nó nói với người gọi, "Foo sẽ không * thay đổi nội dung của tham số đó." Truyền tham chiếu const cũng cho phép trình biên dịch đưa ra các quyết định hiệu suất nhất định.

*: Trừ khi nó bỏ đi tính chất, nhưng đó là một bài viết khác.


3
Đó không phải là những gì câu hỏi này là về; tất nhiên đối với các đối số được tham chiếu hoặc trỏ tới, đó là một ý tưởng tốt để sử dụng const (nếu giá trị được tham chiếu hoặc hướng tới không được sửa đổi). Lưu ý rằng đó không phải là tham số const trong ví dụ con trỏ của bạn; đó là điều mà tham số trỏ đến đó là.
tml

> Truyền tham chiếu const cũng cho phép trình biên dịch đưa ra các quyết định hiệu suất nhất định. ngụy biện cổ điển - trình biên dịch phải tự xác định hằng số, từ khóa const không giúp ích gì cho điều đó nhờ vào bí danh con trỏ và const_cast
jheriko

73

Bổ sung siêu thừa là xấu từ quan điểm API:

Việc đặt thêm các hằng số thừa trong mã của bạn cho các tham số loại nội tại được truyền bởi giá trị sẽ khóa API của bạn trong khi không đưa ra lời hứa có ý nghĩa với người gọi hoặc người dùng API (điều này chỉ cản trở việc triển khai).

Quá nhiều 'const' trong một API khi không cần thiết giống như " sói khóc ", cuối cùng mọi người sẽ bắt đầu phớt lờ 'const' bởi vì nó ở khắp mọi nơi và hầu như không có nghĩa là hầu hết thời gian.

Đối số "reductio ad absurdum" với các hằng số bổ sung trong API rất tốt cho hai điểm đầu tiên này là nếu nhiều tham số const tốt hơn, thì mọi đối số có thể có một hằng số trên nó, NÊN có một hằng số trên nó. Trong thực tế, nếu nó thực sự tốt, bạn muốn const là mặc định cho các tham số và chỉ có một từ khóa như "có thể thay đổi" khi bạn muốn thay đổi tham số.

Vì vậy, hãy thử đặt vào bất cứ nơi nào chúng ta có thể:

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

Hãy xem xét dòng mã ở trên. Tuyên bố không chỉ lộn xộn hơn và dài hơn và khó đọc hơn mà ba trong số bốn từ khóa 'const' có thể được người dùng API bỏ qua một cách an toàn. Tuy nhiên, việc sử dụng thêm 'const' đã khiến dòng thứ hai có khả năng NGUY HIỂM!

Tại sao?

Việc đọc sai tham số đầu tiên char * const buffercó thể khiến bạn nghĩ rằng nó sẽ không sửa đổi bộ nhớ trong bộ đệm dữ liệu được truyền vào - tuy nhiên, điều này không đúng! 'Const' không cần thiết có thể dẫn đến các giả định nguy hiểm và không chính xác về API của bạn khi được quét hoặc đọc sai nhanh chóng.


Các hằng số thừa cũng không tốt theo quan điểm Thực thi Mã:

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

Nếu FLEXIBLE_IMPLEMENTATION không đúng, thì API đang hứa hẹn với việc không thực hiện chức năng theo cách đầu tiên bên dưới.

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

Đó là một lời hứa rất ngớ ngẩn để thực hiện. Tại sao bạn nên thực hiện một lời hứa không mang lại lợi ích nào cho người gọi và chỉ giới hạn việc thực hiện của bạn?

Cả hai đều là các triển khai hoàn toàn hợp lệ của cùng một chức năng mặc dù vậy tất cả những gì bạn đã thực hiện được buộc một tay sau lưng một cách không cần thiết.

Hơn nữa, đó là một lời hứa rất nông cạn dễ dàng (và bị phá vỡ một cách hợp pháp).

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

Hãy nhìn xem, dù sao thì tôi cũng đã thực hiện theo cách đó mặc dù tôi đã hứa là không - chỉ sử dụng chức năng bao bọc. Giống như khi kẻ xấu hứa sẽ không giết ai đó trong phim và ra lệnh cho tay sai của mình giết họ.

Những chòm sao thừa thãi đó đáng giá hơn một lời hứa từ một kẻ xấu trong phim.


Nhưng khả năng nói dối thậm chí còn tồi tệ hơn:

Tôi đã được khai sáng rằng bạn có thể không khớp const trong tiêu đề (khai báo) và mã (định nghĩa) bằng cách sử dụng const giả. Những người ủng hộ const-happy tuyên bố đây là một điều tốt vì nó cho phép bạn đặt const chỉ trong định nghĩa.

// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }

Tuy nhiên, điều ngược lại là đúng ... bạn chỉ có thể đặt một hằng số giả trong khai báo và bỏ qua nó trong định nghĩa. Điều này chỉ làm cho các const không cần thiết trong một API trở nên khủng khiếp hơn và một lời nói dối khủng khiếp - xem ví dụ này:

class foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

Tất cả các const không cần thiết thực sự là làm cho mã của người triển khai ít đọc hơn bằng cách buộc anh ta sử dụng một bản sao cục bộ hoặc hàm bao bọc khác khi anh ta muốn thay đổi biến hoặc chuyển biến bằng tham chiếu không const.

Nhìn vào ví dụ này. Cái nào dễ đọc hơn? Rõ ràng rằng lý do duy nhất cho biến phụ trong hàm thứ hai là do một số nhà thiết kế API đã ném vào một hằng số thừa?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

Hy vọng chúng tôi đã học được điều gì đó ở đây. Không cần thiết là một đôi mắt lộn xộn API, một lời cằn nhằn khó chịu, một lời hứa nông cạn và vô nghĩa, một trở ngại không cần thiết và đôi khi dẫn đến những sai lầm rất nguy hiểm.


9
Tại sao các downvote? Sẽ hữu ích hơn nhiều nếu bạn để lại một bình luận ngắn gọn về một downvote.
Adisak

7
Toàn bộ quan điểm của việc sử dụng đối số const là làm cho dòng được đánh dấu không thành công (plist = pnext). Đó là một biện pháp an toàn hợp lý để giữ cho đối số chức năng không thay đổi. Tôi đồng ý với quan điểm của bạn rằng chúng rất tệ trong các khai báo hàm (vì chúng rất thừa), nhưng chúng có thể phục vụ các mục đích của chúng trong khối triển khai.
touko

23
@Adisak Tôi không thấy có gì sai với câu trả lời của bạn, nhưng có vẻ như từ nhận xét của bạn rằng bạn đang thiếu một điểm quan trọng. Định nghĩa / thực hiện chức năng không phải là một phần của API, đây chỉ là khai báo hàm . Như bạn đã nói, khai báo các hàm với tham số const là vô nghĩa và thêm lộn xộn. Tuy nhiên, người dùng API có thể không bao giờ cần phải xem triển khai của nó. Trong khi đó, người triển khai có thể quyết định đủ điều kiện cấu thành một số tham số trong định nghĩa hàm CHỈ cho rõ ràng, điều này là hoàn toàn tốt.
jw013

17
@ jw013 là chính xác, void foo(int)void foo(const int)là cùng một chức năng, không quá tải. ideone.com/npN4W4 ideone.com/tZav9R const ở đây chỉ là một chi tiết triển khai của thân hàm và không có tác dụng đối với độ phân giải quá tải. Để const ra khỏi khai báo, cho API an toàn hơn và gọn gàng hơn, nhưng đặt const vào định nghĩa , nếu bạn có ý định không sửa đổi giá trị được sao chép.
Oktalist

3
@Adisak Tôi biết điều này đã cũ, nhưng tôi tin rằng việc sử dụng chính xác cho API công khai sẽ là cách khác. Bằng cách đó, các nhà phát triển làm việc trên các bộ phận bên trong sẽ không phạm sai lầm như pi++khi họ không cần phải làm vậy.
CoffeeandCode

39

const nên được mặc định trong C ++. Như thế này :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable

8
Chính xác là suy nghĩ của tôi.
Constantin

24
Khả năng tương thích với C là quá quan trọng, ít nhất là đối với những người thiết kế C ++, thậm chí xem xét điều này.

4
Thú vị, tôi chưa bao giờ nghĩ về điều đó.
Dan

6
Tương tự, đáng unsignedlẽ phải là mặc định trong C ++. Như thế này: int i = 5; // i is unsignedsigned int i = 5; // i is signed.
hkBattousai

25

Khi tôi mã hóa C ++ để kiếm sống, tôi đã tạo ra mọi thứ có thể. Sử dụng const là một cách tuyệt vời để giúp trình biên dịch giúp bạn. Chẳng hạn, tạo các giá trị trả về phương thức của bạn có thể cứu bạn khỏi các lỗi chính tả, chẳng hạn như:

foo() = 42

khi bạn có nghĩa là:

foo() == 42

Nếu foo () được xác định để trả về tham chiếu không phải là const:

int& foo() { /* ... */ }

Trình biên dịch sẽ vui vẻ cho phép bạn gán một giá trị cho tạm thời ẩn danh được gọi bởi hàm gọi. Làm cho nó const:

const int& foo() { /* ... */ }

Loại bỏ khả năng này.


6
Với trình biên dịch nào đã làm việc đó? GCC đưa ra lỗi trong khi cố gắng biên dịch foo() = 42: error: lvalue được yêu cầu dưới dạng toán hạng trái của phép gán
gavrie

Điều này chỉ không chính xác. foo () = 42 giống với 2 = 3, tức là lỗi trình biên dịch. Và trả lại một const là hoàn toàn vô nghĩa. Nó không làm bất cứ điều gì cho một loại tích hợp.
Josh

2
Tôi đã gặp phải cách sử dụng const này và tôi có thể nói với bạn, cuối cùng nó tạo ra nhiều rắc rối hơn là lợi ích. Gợi ý: const int foo()thuộc loại khác int foo(), điều này khiến bạn gặp rắc rối lớn nếu bạn đang sử dụng những thứ như con trỏ chức năng, hệ thống tín hiệu / khe cắm hoặc boost :: bind.
Mephane

2
Tôi đã sửa mã để bao gồm giá trị trả về tham chiếu.
Avdi

Không const int& foo()hiệu quả giống như int foo(), do tối ưu hóa giá trị trả về?
Zantier

15

Có một cuộc thảo luận tốt về chủ đề này trong các bài viết "Chuyên gia của Tuần" cũ trên comp.lang.c ++. Được kiểm duyệt ở đây .

Bài viết GOTW tương ứng có sẵn trên trang web của Herb Sutter tại đây .


1
Herb Sutter là một chàng trai thực sự thông minh :-) Chắc chắn đáng để đọc và tôi đồng ý với TẤT CẢ các điểm của anh ấy.
Adisak

2
Bài viết hay nhưng tôi không đồng ý với anh ấy về các cuộc tranh luận. Tôi làm cho chúng là const vì chúng giống như các biến và tôi không bao giờ muốn bất kỳ ai thực hiện bất kỳ thay đổi nào đối với các đối số của tôi.
QBziZ

9

Tôi nói const tham số giá trị của bạn.

Hãy xem xét chức năng lỗi này:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

Nếu tham số số là const, trình biên dịch sẽ dừng và cảnh báo chúng tôi về lỗi.


2
một cách khác là sử dụng if (0 == number) ... other ...;
Julian Schaub - litb

5
@ ChrisHuang-Leaver Thật kinh khủng phải không, nếu nói như Yoda bạn làm: stackoverflow.com/a/2430307/210916
MPelletier

GCC / Clang -Wall cung cấp cho bạn -Wparentheses, yêu cầu bạn thực hiện "if ((number = 0))" nếu đó thực sự là những gì bạn định làm. Mà hoạt động tốt như là một thay thế cho là Yoda.
Jetski S loại

8

Tôi sử dụng const trên các tham số hàm là tham chiếu (hoặc con trỏ) chỉ là dữ liệu [in] và sẽ không được sửa đổi bởi hàm. Có nghĩa là, khi mục đích của việc sử dụng tham chiếu là để tránh sao chép dữ liệu và không cho phép thay đổi tham số đã truyền.

Đặt const trên tham số boolean b trong ví dụ của bạn chỉ đặt ra một ràng buộc đối với việc triển khai và không đóng góp cho giao diện của lớp (mặc dù thường không nên thay đổi tham số).

Chữ ký hàm cho

void foo(int a);

void foo(const int a);

là như nhau, điều này giải thích .c và .h của bạn

Asaf


6

Nếu bạn sử dụng ->*hoặc các .*toán tử, đó là phải.

Nó ngăn bạn viết một cái gì đó như

void foo(Bar *p) { if (++p->*member > 0) { ... } }

điều mà tôi gần như đã làm ngay bây giờ và có lẽ không làm được những gì bạn dự định.

Điều tôi dự định nói là

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

và nếu tôi đã đặt một constở giữa Bar *p, trình biên dịch sẽ nói với tôi điều đó.


4
Tôi sẽ ngay lập tức kiểm tra một tài liệu tham khảo về quyền ưu tiên của nhà điều hành khi tôi sắp kết hợp nhiều nhà khai thác (nếu tôi chưa biết 100%), vì vậy IMO không phải là vấn đề.
mk12

5

Ah, một khó khăn. Một mặt, một tuyên bố là một hợp đồng và nó thực sự không có ý nghĩa để vượt qua một đối số const theo giá trị. Mặt khác, nếu bạn nhìn vào việc thực hiện chức năng, bạn cung cấp cho trình biên dịch nhiều cơ hội để tối ưu hóa nếu bạn khai báo một đối số không đổi.


5

const là vô nghĩa khi đối số được truyền theo giá trị vì bạn sẽ không sửa đổi đối tượng của trình gọi.

const nên được ưu tiên khi truyền bằng tham chiếu, trừ khi mục đích của hàm là sửa đổi giá trị được truyền.

Cuối cùng, một hàm không sửa đổi đối tượng hiện tại (cái này) có thể, và có lẽ nên được khai báo const. Một ví dụ dưới đây:

int SomeClass::GetValue() const {return m_internalValue;}

Đây là một lời hứa sẽ không sửa đổi đối tượng mà cuộc gọi này được áp dụng. Nói cách khác, bạn có thể gọi:

const SomeClass* pSomeClass;
pSomeClass->GetValue();

Nếu hàm không phải là const, điều này sẽ dẫn đến cảnh báo trình biên dịch.


5

Đánh dấu các tham số giá trị 'const' chắc chắn là một điều chủ quan.

Tuy nhiên tôi thực sự thích đánh dấu các tham số giá trị const, giống như trong ví dụ của bạn.

void func(const int n, const long l) { /* ... */ }

Giá trị với tôi là chỉ rõ rằng các giá trị tham số hàm không bao giờ được thay đổi bởi hàm. Chúng sẽ có cùng giá trị ở đầu như ở cuối. Đối với tôi, nó là một phần của việc giữ một kiểu lập trình rất chức năng.

Đối với một hàm ngắn, có thể lãng phí thời gian / không gian để có 'const' ở đó, vì thông thường khá rõ ràng là các đối số không được sửa đổi bởi hàm.

Tuy nhiên, đối với một hàm lớn hơn, nó là một dạng tài liệu triển khai và nó được thực thi bởi trình biên dịch.

Tôi có thể chắc chắn nếu tôi thực hiện một số tính toán với 'n' và 'l', tôi có thể cấu trúc lại / di chuyển tính toán đó mà không sợ nhận được kết quả khác vì tôi đã bỏ lỡ một nơi thay đổi một hoặc cả hai.

Vì nó là một chi tiết triển khai, bạn không cần phải khai báo các tham số giá trị const trong tiêu đề, giống như bạn không cần khai báo các tham số hàm có cùng tên với cách sử dụng.


4

Có thể đây sẽ không phải là một đối số hợp lệ. nhưng nếu chúng ta tăng giá trị của biến const trong trình biên dịch hàm sẽ báo lỗi: " error: tăng tham số chỉ đọc ". vì vậy điều đó có nghĩa là chúng ta có thể sử dụng từ khóa const như một cách để ngăn chặn việc vô tình sửa đổi các biến của chúng ta bên trong các hàm (mà chúng ta không được phép / chỉ đọc). Vì vậy, nếu chúng ta vô tình làm điều đó tại trình biên dịch thời gian biên dịch sẽ cho chúng ta biết điều đó. điều này đặc biệt quan trọng nếu bạn không phải là người duy nhất đang làm việc trong dự án này.


3

Tôi có xu hướng sử dụng const bất cứ nơi nào có thể. (Hoặc từ khóa thích hợp khác cho ngôn ngữ đích.) Tôi làm điều này hoàn toàn vì nó cho phép trình biên dịch thực hiện các tối ưu hóa bổ sung mà nó sẽ không thể thực hiện theo cách khác. Vì tôi không biết những tối ưu hóa này có thể là gì, tôi luôn làm điều đó, ngay cả khi nó có vẻ ngớ ngẩn.

Đối với tất cả những gì tôi biết, trình biên dịch rất có thể nhìn thấy một tham số giá trị const và nói, "Này, hàm này không sửa đổi nó, vì vậy tôi có thể chuyển qua tham chiếu và lưu một số chu kỳ xung nhịp." Tôi không nghĩ nó sẽ làm một việc như vậy, vì nó thay đổi chữ ký hàm, nhưng nó làm cho điểm. Có thể nó thực hiện một số thao tác ngăn xếp khác nhau hoặc một cái gì đó ... Vấn đề là, tôi không biết, nhưng tôi biết cố gắng thông minh hơn trình biên dịch chỉ khiến tôi xấu hổ.

C ++ có một số hành lý bổ sung, với ý tưởng về tính chính xác, vì vậy nó càng trở nên quan trọng hơn.


Mặc dù nó có thể giúp ích trong một số trường hợp, tôi nghi ngờ khả năng thúc đẩy sự tối ưu hóa bị cường điệu hóa quá mức là một lợi ích của const. Thay vào đó, đó là vấn đề nêu rõ ý định trong quá trình thực hiện và bắt các ý nghĩ sau này (vô tình làm tăng biến cục bộ sai, vì không phải vậy const). Song song, tôi cũng nói thêm rằng các trình biên dịch rất hoan nghênh thay đổi chữ ký hàm, theo nghĩa là các hàm có thể được nội tuyến và một khi đã nội tuyến thì toàn bộ cách chúng hoạt động có thể được thay đổi; thêm hoặc xóa tham chiếu, tạo chữ 'biến', v.v ... đều nằm trong quy tắc as-if
underscore_d

3

1. Câu trả lời hay nhất dựa trên đánh giá của tôi:

Câu trả lời của @Adisak là câu trả lời tốt nhất ở đây dựa trên đánh giá của tôi. Lưu ý rằng câu trả lời này là một phần tốt nhất bởi vì nó cũng là sự nổi-backed-up với các ví dụ mã thực , ngoài việc sử dụng âm thanh và logic cũng suy nghĩ ra.

2. Từ của riêng tôi (đồng ý với câu trả lời hay nhất):

  1. Đối với giá trị vượt qua không có lợi ích để thêm const. Tất cả những gì nó làm là:
    1. hạn chế người triển khai phải tạo một bản sao mỗi lần họ muốn thay đổi thông số đầu vào trong mã nguồn (dù sao thay đổi đó sẽ không có tác dụng phụ vì những gì được truyền vào đã là bản sao vì nó là giá trị truyền qua). Và thường xuyên, việc thay đổi một thông số đầu vào được truyền bằng giá trị được sử dụng để thực hiện chức năng, vì vậy việc thêm vào constmọi nơi có thể cản trở điều này.
    2. và thêm constmã không cần thiết vào mã constở mọi nơi, thu hút sự chú ý khỏi các mã constthực sự cần thiết để có mã an toàn.
  2. Tuy nhiên, khi xử lý các con trỏ hoặc tham chiếuconst là cực kỳ quan trọng khi cần và phải được sử dụng, vì nó ngăn chặn các tác dụng phụ không mong muốn với các thay đổi liên tục bên ngoài chức năng, và do đó, mỗi con trỏ hoặc tham chiếu phải sử dụng constkhi parm chỉ là đầu vào, không phải là một đầu ra. const Chỉ sử dụng trên các tham số được truyền bởi tham chiếu hoặc con trỏ có lợi ích bổ sung là làm cho nó thực sự rõ ràng tham số nào là con trỏ hoặc tham chiếu. Đó là một điều nữa để nói và nói "Xem ra! Bất kỳ thông số nào constbên cạnh nó là một tham chiếu hoặc con trỏ!".
  3. Những gì tôi đã mô tả ở trên thường là sự đồng thuận đạt được trong các tổ chức phần mềm chuyên nghiệp mà tôi đã làm việc và được coi là thực tiễn tốt nhất. Đôi khi, quy tắc rất nghiêm ngặt: "đừng bao giờ sử dụng const trên các tham số được truyền theo giá trị, mà luôn sử dụng nó trên các tham số được truyền bởi tham chiếu hoặc con trỏ nếu chúng chỉ là đầu vào."

3. Từ của Google (đồng ý với tôi và câu trả lời hay nhất):

(Từ " Hướng dẫn về Phong cách Google C ++ ")

Đối với một tham số hàm được truyền bởi giá trị, const không có tác dụng đối với người gọi, do đó không được khuyến nghị trong khai báo hàm. Xem TotW # 109 .

Sử dụng const trên các biến cục bộ không được khuyến khích cũng không được khuyến khích.

Nguồn: phần "Sử dụng const" trong Hướng dẫn về Phong cách Google C ++: https://google.github.io/styleguide/cppguide.html#Use_of_const . Đây thực sự là một phần thực sự có giá trị, vì vậy hãy đọc toàn bộ phần.

Lưu ý rằng "TotW # 109" là viết tắt của "Mẹo của Tuần # 109: Có ý nghĩa consttrong Tuyên bố Chức năng" , và cũng là một bài đọc hữu ích. Nó có nhiều thông tin hơn và ít quy định hơn về những việc cần làm và dựa trên ngữ cảnh được đưa ra trước quy tắc Hướng dẫn về Phong cách Google C ++ constđược trích dẫn ở trên, nhưng do sự rõ ràng mà nó cung cấp, constquy tắc được trích dẫn ở trên đã được thêm vào Google C ++ Hướng dẫn mẫu.

Cũng lưu ý rằng ngay cả khi tôi trích dẫn Hướng dẫn về Phong cách Google C ++ ở đây để bảo vệ vị trí của mình, điều đó KHÔNG có nghĩa là tôi luôn làm theo hướng dẫn hoặc luôn khuyên bạn nên làm theo hướng dẫn. Một số điều họ đề xuất chỉ đơn giản là kỳ lạ, chẳng hạn như quy ước đặt tên theo kiểu của họ kDaysInAWeekcho "Tên không đổi" . Tuy nhiên, dù sao vẫn hữu ích và có liên quan để chỉ ra khi một trong những công ty phần mềm và kỹ thuật có ảnh hưởng và thành công nhất thế giới sử dụng cùng một biện minh như tôi và những người khác như @Adisak làm để sao lưu quan điểm của chúng tôi về vấn đề này.

4. Kẻ nói dối của Clang clang-tidy, có một số tùy chọn cho việc này:

A. Cũng đáng lưu ý rằng kẻ nói dối của Clang clang-tidy, có một tùy chọn readability-avoid-const-params-in-decls, được mô tả ở đây , để hỗ trợ thực thi trong một cơ sở mã không sử dụng constcho các tham số hàm truyền qua giá trị :

Kiểm tra xem một khai báo hàm có các tham số là const cấp cao nhất không.

giá trị const trong khai báo không ảnh hưởng đến chữ ký của hàm, vì vậy chúng không nên được đặt ở đó.

Ví dụ:

void f(const string);   // Bad: const is top level.
void f(const string&);  // Good: const is not top level.

Và đây là hai ví dụ nữa tôi tự thêm vào cho đầy đủ và rõ ràng:

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

B. Nó cũng có tùy chọn này: readability-const-return-type- https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

5. Cách tiếp cận thực tế của tôi về cách tôi hướng dẫn phong cách về vấn đề này:

Tôi chỉ cần sao chép và dán nó vào hướng dẫn phong cách của tôi:

[SAO CHÉP / PASTE BẮT ĐẦU]

  1. Luôn sử dụng const trên các tham số chức năng được truyền bởi tham chiếu hoặc con trỏ khi nội dung của chúng (những gì chúng trỏ đến) được dự định KHÔNG được thay đổi. Theo cách này, nó trở nên rõ ràng khi một biến được truyền bởi tham chiếu hoặc con trỏ IS dự kiến ​​sẽ được thay đổi, bởi vì nó sẽ thiếu const. Trong trường hợp sử dụng này constngăn ngừa tác dụng phụ ngẫu nhiên bên ngoài chức năng.
  2. Không nên sử dụng consttrên các tham số chức năng được truyền bởi giá trị, bởi vì constkhông có tác dụng đối với người gọi: ngay cả khi biến được thay đổi trong chức năng, sẽ không có tác dụng phụ bên ngoài chức năng. Xem các tài nguyên sau để biết thêm lý do và cái nhìn sâu sắc:
    1. Phần "Hướng dẫn về phong cách Google C ++" "Sử dụng const"
    2. "Mẹo của Tuần # 109: Có ý nghĩa consttrong Tuyên bố Chức năng"
    3. Câu trả lời chồng chéo của Adisak về "Sử dụng 'const' cho các tham số chức năng"
  3. " Không bao giờ sử dụng cấp cao nhất const[tức là: consttrên các tham số được truyền bởi giá trị ] cho các tham số hàm trong các khai báo không phải là định nghĩa (và cẩn thận không sao chép / dán vô nghĩa const). Nó là vô nghĩa và bị bỏ qua bởi trình biên dịch, đó là nhiễu hình ảnh và nó có thể đánh lừa độc giả "( https://abseil.io/tips/109 , nhấn mạnh thêm).
    1. Duy nhất const vòng loại có ảnh hưởng đến quá trình biên dịch là các vòng loại được đặt trong định nghĩa hàm, KHÔNG phải là các khai báo chuyển tiếp của hàm, chẳng hạn như trong khai báo hàm (phương thức) trong tệp tiêu đề.
  4. Không bao giờ sử dụng cấp cao nhất const[tức là: consttrên các biến được truyền bởi giá trị ] trên các giá trị được trả về bởi một hàm.
  5. Sử dụng const trên các con trỏ hoặc các tham chiếu được trả về bởi một hàm là tùy thuộc vào người triển khai , vì đôi khi nó rất hữu ích.
  6. TODO: thi hành một số điều trên với những điều sau đây clang-tidy tùy chọn :
    1. https://clang.llvm.org/extra/clang-tidy/checks/readability-avoid-const-params-in-decls.html
    2. https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

Dưới đây là một số ví dụ mã để chứng minh const quy tắc được mô tả ở trên:

constVí dụ về tham số:
(một số được mượn từ đây )

void f(const std::string);   // Bad: const is top level.
void f(const std::string&);  // Good: const is not top level.

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

constVí dụ về loại trả về:
(một số được mượn từ đây )

// BAD--do not do this:
const int foo();
const Clazz foo();
Clazz *const foo();

// OK--up to the implementer:
const int* foo();
const int& foo();
const Clazz* foo();

[SAO CHÉP / PASTE END]


2

Trong trường hợp bạn đề cập, nó không ảnh hưởng đến người gọi API của bạn, đó là lý do tại sao nó không được thực hiện phổ biến (và không cần thiết trong tiêu đề). Nó chỉ ảnh hưởng đến việc thực hiện chức năng của bạn.

Đó không phải là điều đặc biệt xấu, nhưng những lợi ích không phải là điều tuyệt vời mà nó không ảnh hưởng đến API của bạn và nó thêm vào việc gõ, vì vậy nó thường không được thực hiện.


2

Tôi không sử dụng const cho parametere thông qua giá trị. Người gọi không quan tâm bạn có sửa đổi tham số hay không, đó là một chi tiết triển khai.

Điều thực sự quan trọng là đánh dấu các phương thức là const nếu chúng không sửa đổi thể hiện của chúng. Làm điều này khi bạn đi, bởi vì nếu không, bạn có thể kết thúc với rất nhiều const_cast <> hoặc bạn có thể thấy rằng việc đánh dấu một phương thức const yêu cầu thay đổi rất nhiều mã bởi vì nó gọi các phương thức khác nên được đánh dấu const.

Tôi cũng có xu hướng đánh dấu các vars const cục bộ nếu tôi không cần sửa đổi chúng. Tôi tin rằng nó làm cho mã dễ hiểu hơn bằng cách làm cho nó dễ dàng hơn để xác định "các bộ phận chuyển động".



2

Tôi sử dụng const là tôi có thể. Const cho tham số có nghĩa là chúng không nên thay đổi giá trị của chúng. Điều này đặc biệt có giá trị khi đi qua tham chiếu. const cho hàm tuyên bố rằng hàm không nên thay đổi các thành viên lớp.


2

Để tóm tắt:

  • "Thông thường const pass-by-value là không đáng tin cậy và gây hiểu lầm ở mức tốt nhất." Từ GOTW006
  • Nhưng bạn có thể thêm chúng vào .cpp như bạn sẽ làm với các biến.
  • Lưu ý rằng thư viện chuẩn không sử dụng const. Ví dụ std::vector::at(size_type pos). Những gì đủ tốt cho thư viện tiêu chuẩn là tốt cho tôi.

2
"Những gì đủ tốt cho thư viện tiêu chuẩn là tốt cho tôi" không phải lúc nào cũng đúng. Ví dụ: thư viện tiêu chuẩn sử dụng các tên biến xấu xí như _Tmpmọi lúc - bạn không muốn điều này (thực ra bạn không được phép sử dụng chúng).
anatolyg

1
@anatolyg đây là chi tiết triển khai
Fernando Pelliccioni

2
OK, cả tên biến và loại đủ điều kiện trong danh sách đối số đều là chi tiết triển khai. Điều tôi muốn nói là, việc thực hiện thư viện chuẩn đôi khi không tốt. Đôi khi, bạn có thể (và nên) làm tốt hơn. Khi nào mã cho thư viện chuẩn được viết - 10 năm trước? 5 năm trước (một số phần mới nhất của nó)? Chúng ta có thể viết mã tốt hơn ngày hôm nay.
anatolyg

1

Nếu tham số được truyền theo giá trị (và không phải là tham chiếu), thông thường sẽ không có nhiều khác biệt cho dù tham số có được khai báo là const hay không (trừ khi nó chứa thành viên tham chiếu - không phải là vấn đề đối với các loại tích hợp). Nếu tham số là tham chiếu hoặc con trỏ, thông thường tốt hơn là bảo vệ bộ nhớ được tham chiếu / trỏ đến chứ không phải chính con trỏ (tôi nghĩ bạn không thể tạo tham chiếu chính nó, không quan trọng lắm vì bạn không thể thay đổi trọng tài) . Có vẻ là một ý tưởng tốt để bảo vệ mọi thứ bạn có thể như const. Bạn có thể bỏ qua nó mà không sợ mắc lỗi nếu các tham số chỉ là POD (bao gồm các loại tích hợp) và không có khả năng chúng thay đổi thêm dọc theo con đường (ví dụ trong ví dụ của bạn là tham số bool).

Tôi không biết về sự khác biệt khai báo tệp .h / .cpp, nhưng nó có ý nghĩa gì đó. Ở cấp độ mã máy, không có gì là "const", vì vậy nếu bạn khai báo một hàm (trong .h) là không phải là const, mã giống như khi bạn khai báo nó là const (tối ưu hóa sang một bên). Tuy nhiên, nó giúp bạn tranh thủ trình biên dịch mà bạn sẽ không thay đổi giá trị của biến bên trong việc thực hiện hàm (.ccp). Nó có thể có ích trong trường hợp khi bạn kế thừa từ một giao diện cho phép thay đổi, nhưng bạn không cần thay đổi thành tham số để đạt được chức năng cần thiết.


0

Tôi sẽ không đặt const trên các tham số như thế - mọi người đều biết rằng một boolean (trái ngược với boolean &) là không đổi, vì vậy việc thêm nó vào sẽ khiến mọi người nghĩ "chờ đợi, cái gì?" hoặc thậm chí là bạn đang truyền tham số bằng tham chiếu.


4
đôi khi bạn muốn vượt qua một đối tượng bằng cách tham chiếu (vì lý do hiệu suất) nhưng không thay đổi nó, vì vậy const là bắt buộc sau đó. Giữ tất cả các tham số như vậy - ngay cả các bool - const sẽ là cách thực hành tốt, giúp mã của bạn dễ đọc hơn.
gbjbaanb

0

Điều cần nhớ với const là việc tạo ra mọi thứ từ đầu dễ dàng hơn nhiều so với việc thử và đặt chúng vào sau.

Sử dụng const khi bạn muốn một cái gì đó không thay đổi - đó là một gợi ý được thêm vào để mô tả chức năng của bạn làm gì và những gì mong đợi. Tôi đã thấy nhiều API C có thể làm với một số trong số chúng, đặc biệt là các API chấp nhận chuỗi c!

Tôi có xu hướng bỏ qua từ khóa const trong tệp cpp hơn tiêu đề, nhưng khi tôi có xu hướng cắt + dán chúng, chúng sẽ được giữ ở cả hai vị trí. Tôi không biết tại sao trình biên dịch cho phép điều đó, tôi đoán đó là một trình biên dịch. Thực hành tốt nhất là chắc chắn để đặt từ khóa const của bạn trong cả hai tập tin.


Tôi không nhận được điều này cả. Tại sao bạn có xu hướng bỏ qua nó trong tệp cpp (định nghĩa hàm)? Đó là nơi nó thực sự có nghĩa là một cái gì đó và có thể bắt lỗi. Tại sao bạn nghĩ rằng đó là cách tốt nhất để đặt const ở cả hai nơi? Trong tệp tiêu đề (khai báo hàm), nó có nghĩa là không có gì và làm mờ API. Có thể có một số giá trị nhỏ để có phần giảm và trông giống hệt nhau, nhưng đối với tôi, đó có vẻ là một lợi ích thực sự nhỏ so với vấn đề làm lộn xộn API.
Don nở

@DonHatch 8 năm sau, wow. Dù sao, như OP đã nói "Tôi cũng rất ngạc nhiên khi biết rằng bạn có thể bỏ qua const từ các tham số trong khai báo hàm nhưng có thể đưa nó vào định nghĩa hàm".
gbjbaanb

0

Thực sự không có lý do để tạo "const" tham số giá trị vì hàm chỉ có thể sửa đổi một bản sao của biến.

Lý do để sử dụng "const" là nếu bạn chuyển một cái gì đó lớn hơn (ví dụ: một cấu trúc có nhiều thành viên) bằng cách tham chiếu, trong trường hợp đó, nó đảm bảo rằng hàm không thể sửa đổi nó; hay đúng hơn, trình biên dịch sẽ phàn nàn nếu bạn cố gắng sửa đổi nó theo cách thông thường. Nó ngăn chặn nó vô tình được sửa đổi.


0

Tham số Const chỉ hữu ích khi tham số được truyền bởi tham chiếu tức là tham chiếu hoặc con trỏ. Khi trình biên dịch nhìn thấy một tham số const, nó đảm bảo rằng biến được sử dụng trong tham số không bị sửa đổi trong phần thân của hàm. Tại sao bất cứ ai cũng muốn làm cho một tham số theo giá trị là hằng số? :-)


Vì nhiều lý do. Tạo một tham số theo giá trị constrõ ràng: 'Tôi không cần sửa đổi điều này, vì vậy tôi đang tuyên bố điều đó. Nếu tôi cố gắng sửa đổi nó sau, hãy cho tôi một lỗi thời gian biên dịch để tôi có thể sửa lỗi hoặc bỏ đánh dấu là const. ' Vì vậy, đó là vấn đề của cả vệ sinh mã và an toàn. Đối với tất cả những gì nó cần để thêm vào các tệp thực hiện, nó phải là thứ mà mọi người làm như một phản xạ thuần túy, IMO.
gạch dưới

0

Vì các tham số đang được truyền theo giá trị, nó không tạo ra bất kỳ sự khác biệt nào nếu bạn chỉ định const hoặc không từ phối cảnh của hàm gọi. Về cơ bản, không có ý nghĩa gì khi khai báo truyền bởi các tham số giá trị là const.


0

Tất cả các hằng số trong ví dụ của bạn không có mục đích. C ++ theo mặc định là giá trị truyền qua, vì vậy hàm sẽ nhận được các bản sao của các int và booleans đó. Ngay cả khi chức năng không sửa đổi chúng, bản sao của trình gọi không bị ảnh hưởng.

Vì vậy, tôi sẽ tránh các hằng số thêm bởi vì

  • Họ đang nói xấu
  • Họ làm lộn xộn văn bản
  • Chúng ngăn tôi thay đổi giá trị được thông qua trong trường hợp nó có thể hữu ích hoặc hiệu quả.

-1

Tôi biết câu hỏi là "một chút" lỗi thời nhưng khi tôi đi qua nó, một người khác cũng có thể làm như vậy trong tương lai ... ... tôi vẫn nghi ngờ người đồng nghiệp nghèo sẽ liệt kê xuống đây để đọc bình luận của tôi :)

Dường như với tôi rằng chúng ta vẫn còn quá giới hạn trong lối suy nghĩ theo kiểu C. Trong nghịch lý OOP, chúng ta chơi xung quanh với các đối tượng, không phải các loại. Đối tượng Const có thể khác về mặt khái niệm với một đối tượng không phải là const, cụ thể theo nghĩa logic-const (trái ngược với bitwise-const). Do đó, ngay cả khi độ chính xác của các tham số hàm là (có lẽ) là một sự cẩn thận quá mức trong trường hợp POD, nó không phải là như vậy trong trường hợp của các đối tượng. Nếu một hàm làm việc với một đối tượng const thì nó sẽ nói như vậy. Hãy xem xét đoạn mã sau

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps.: bạn có thể lập luận rằng (const) tham chiếu sẽ phù hợp hơn ở đây và cung cấp cho bạn hành vi tương tự. Vâng, đúng rồi. Chỉ cần đưa ra một hình ảnh khác với những gì tôi có thể thấy ở nơi khá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.