从API的角度来看,多余的const是不利的:
在代码中为值传递的内在类型参数添加多余的const 会使您的API混乱,而对调用者或API用户没有任何有意义的承诺(这只会妨碍实现)。
当不需要时,API中太多的“ const”就像“ 哭泣的狼 ”,最终人们会开始忽略“ const”,因为它遍地都是,在大多数时候没有任何意义。
API中额外的const的“ reducio 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不为true,则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
class foo { void test(int *pi); };
void foo::test(int * const pi) { }
但是,反之亦然。您可以仅在声明中添加伪常量,而在定义中忽略它。这只会使API中的多余const变得更可怕,更可怕-参见以下示例:
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
}
多余的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显得杂乱无章,令人讨厌的烦恼,浅薄而毫无意义的承诺,不必要的障碍,并偶尔会导致非常危险的错误。