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 buffer
có 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.