从 API 的角度来看,多余的 const 是不好的:
在代码中为按值传递的内在类型参数添加多余的 const会使您的 API 混乱,同时对调用者或 API 用户没有任何有意义的承诺(它只会妨碍实现)。
API 中不需要的太多“const”就像“狼来了”,最终人们会开始忽略“const”,因为它无处不在,而且大多数时候没有任何意义。
API 中额外 const 的“reductio ad absurdum”参数对于前两点是好的,如果更多的 const 参数是好的,那么每个可以有 const 的参数都应该有 const。事实上,如果它真的那么好,你会希望 const 成为参数的默认值,并且只有在你想更改参数时才有像“mutable”这样的关键字。
因此,让我们尽可能地尝试放入 const :
void mungerum(char * buffer, const char * mask, int count);
void mungerum(char * const buffer, const char * const mask, const int count);
考虑上面的代码行。不仅声明更混乱、更长、更难阅读,而且 API 用户可以安全地忽略四个“const”关键字中的三个。然而,'const' 的额外使用使得第二行可能很危险!
为什么?
对第一个参数的快速误读char * const buffer
可能会让您认为它不会修改传入的数据缓冲区中的内存——然而,事实并非如此!当快速扫描或误读时,多余的 'const' 可能会导致对 API 的危险和错误假设。
从代码实现的角度来看,多余的 const 也是不好的:
#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);
如果 FLEXIBLE_IMPLEMENTATION 不为真,那么 API “承诺”不会以下面的第一种方式实现该功能。
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];
}
}
这是一个非常愚蠢的承诺。为什么你要做出一个对你的调用者没有任何好处并且只会限制你的实现的承诺?
尽管这两者都是相同功能的完全有效的实现,但您所做的只是将一只手不必要地绑在背后。
此外,这是一个很容易(并且在法律上被规避)的非常肤浅的承诺。
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);
}
看,尽管我承诺不这样做,但我还是以这种方式实现了它——只是使用了一个包装函数。这就像当坏人在电影中承诺不杀人并命令他的心腹杀死他们时一样。
那些多余的 const 只值一个电影坏蛋的承诺。
但是说谎的能力变得更糟了:
我已经了解到,您可以通过使用虚假 const 使标头(声明)和代码(定义)中的 const 不匹配。const-happy 的拥护者声称这是一件好事,因为它允许您仅将 const 放在定义中。
// Example of const only in definition, not declaration
struct foo { void test(int *pi); };
void foo::test(int * const pi) { }
但是,反之亦然……您只能在声明中放置虚假的 const 并在定义中忽略它。这只会使 API 中多余的 const 变得更加可怕,而且是可怕的谎言——请看这个例子:
struct 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
}
所有多余的 const 实际上所做的只是通过强制实现者在想要更改变量或通过非 const 引用传递变量时使用另一个本地副本或包装函数来降低实现者的代码的可读性。
看看这个例子。哪个更具可读性?很明显,第二个函数中额外变量的唯一原因是因为某些 API 设计人员投入了多余的 const 吗?
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;
}
}
希望我们在这里学到了一些东西。多余的 const 是一个 API 杂乱无章的讨厌东西,一个烦人的唠叨,一个肤浅而毫无意义的承诺,一个不必要的障碍,并且偶尔会导致非常危险的错误。