你走多远const
?您是否只是const
在必要时制作功能,或者您是否全力以赴并在任何地方使用它?例如,想象一个简单的mutator,它接受一个布尔参数:
void SetValue(const bool b) { my_val_ = b; }
Run Code Online (Sandbox Code Playgroud)
这const
实际上有用吗?我个人选择广泛使用它,包括参数,但在这种情况下,我想知道它是否值得?
我还惊讶地发现你可以省略const
函数声明中的参数,但可以将它包含在函数定义中,例如:
.h文件
void func(int n, long l);
Run Code Online (Sandbox Code Playgroud)
.cpp文件
void func(const int n, const long l)
Run Code Online (Sandbox Code Playgroud)
是否有一个原因?这对我来说似乎有点不寻常.
rle*_*lut 395
"当参数按值传递时,const是没有意义的,因为你不会修改调用者的对象."
错误.
它是关于自我记录您的代码和您的假设.
如果你的代码中有很多人正在处理它并且你的功能是非常重要的,那么你应该将"const"标记为任何内容以及你能做到的一切.在编写工业级代码时,你应该总是假设你的同事是精神病患者试图以任何方式找到你(特别是因为它通常是你自己的未来).
此外,正如之前提到的那样,它可能有助于编译器稍微优化一些事情(虽然这是一个很长的镜头).
Gre*_*ers 172
原因是参数的const仅在函数中本地应用,因为它正在处理数据的副本.这意味着函数签名实际上是相同的.尽管这样做很可能很糟糕.
我个人倾向于不使用const,除了参考和指针参数.对于复制的对象,它并不重要,尽管它可以更安全,因为它在函数内表示意图.这真是一个判断电话.我确实倾向于使用const_iterator但是在循环某些东西时我并不打算修改它,所以我猜他自己的每一个,只要严格保持引用类型的const正确性.
Con*_*tin 148
有时(经常!)我必须解开其他人的C++代码.而且我们都知道其他人的 C++代码几乎是按照定义完全混乱:)所以我做的第一件事就是解密本地数据流,将const放在每个变量定义中,直到编译器开始吠叫.这也意味着const限定值参数,因为它们只是由调用者初始化的奇特局部变量.
嗯,我希望变量是常量默认和可变被要求对非const变量:)
Ben*_*aub 78
以下两行在功能上是等效的:
int foo (int a);
int foo (const int a);
Run Code Online (Sandbox Code Playgroud)
显然a
,foo
如果它是第二种方式定义的话,你将无法在体内进行修改,但与外界没有区别.
凡const
真正派上用场是引用或指针参数:
int foo (const BigStruct &a);
int foo (const BigStruct *a);
Run Code Online (Sandbox Code Playgroud)
这就是foo可以采用一个大参数,也许是一个大小为千兆字节的数据结构,而不是复制它.此外,它告诉调用者,"Foo不会*改变该参数的内容." 传递const引用还允许编译器做出某些性能决策.
*:除非它抛弃了常数,但这是另一篇文章.
Adi*_*sak 66
从API观点来看,额外多余的const是不好的:
在代码中为函数传递的内部类型参数添加额外的多余const 会使您的API混乱,同时对调用者或API用户没有任何有意义的承诺(它只会妨碍实现).
API中太多'const'在不需要的时候就像" 狼来了 ",最终人们会开始忽略'const',因为它遍布整个地方并且在大多数时候都没有意义.
API中额外consts的"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);
Run Code Online (Sandbox Code Playgroud)
考虑上面的代码行.声明更加混乱,更长,更难阅读,但API用户可以安全地忽略四个'const'关键字中的三个.但是,'const'的额外使用使得第二行可能存在危险!
为什么?
快速误读第一个参数char * const buffer
可能会让您认为它不会修改传入的数据缓冲区中的内存 - 但是,这不是真的!多余的"常量"可能会在扫描或快速误读时导致对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);
Run Code Online (Sandbox Code Playgroud)
如果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];
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个非常愚蠢的承诺.你为什么要做出一个承诺,对你的来电者没有任何好处,只会限制你的实施?
这两个都是相同功能的完全有效的实现,但是你所做的就是不必要地将一只手绑在背后.
此外,这是一个非常浅薄的承诺,很容易(并在法律上规避).
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);
}
Run Code Online (Sandbox Code Playgroud)
看,我无论如何都以这种方式实现它,即使我承诺不 - 只是使用包装函数.这就像坏人承诺不会在电影中杀死某人并命令他的追随者杀死他们一样.
那些多余的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) { }
Run Code Online (Sandbox Code Playgroud)
但是,反过来也是如此......你只能在声明中放置一个虚假的const,并在定义中忽略它.这只会使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
}
Run Code Online (Sandbox Code Playgroud)
所有多余的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;
}
}
Run Code Online (Sandbox Code Playgroud)
希望我们在这里学到了一些东西.多余的const是一个混乱的API,一个恼人的唠叨,一个浅薄和毫无意义的承诺,一个不必要的障碍,偶尔会导致非常危险的错误.
QBz*_*ziZ 37
const应该是C++中的默认值.像这样 :
int i = 5 ; // i is a constant
var int i = 5 ; // i is a real variable
Run Code Online (Sandbox Code Playgroud)
Avd*_*vdi 25
当我用C++编写代码时,我尽可能地把所有东西都拿走了.使用const是帮助编译器帮助您的好方法.例如,修改方法返回值可以避免输错,例如:
foo() = 42
Run Code Online (Sandbox Code Playgroud)
当你的意思是:
foo() == 42
Run Code Online (Sandbox Code Playgroud)
如果foo()被定义为返回非const引用:
int& foo() { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
编译器很乐意让你为函数调用返回的匿名临时值赋值.使它成为const:
const int& foo() { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
消除这种可能性.
小智 9
我说const你的值参数.
考虑这个错误的功能:
bool isZero(int number)
{
if (number = 0) // whoops, should be number == 0
return true;
else
return false;
}
Run Code Online (Sandbox Code Playgroud)
如果number参数是const,编译器将停止并警告我们这个bug.
根据我的评估,@Adisak的答案是这里的最佳答案。请注意,这个答案是一部分最好的,因为它也是在最知名的备份与真正的代码示例,除了使用声音和深思熟虑出来的逻辑。
const
. 它所做的只是:
const
随处添加会阻碍这一点。const
不必要地添加const
到处都是 s的代码,从而将注意力从const
真正需要安全代码的s 上移开。const
在需要时非常重要,并且必须使用,因为它可以防止在函数外持续更改带来的不良副作用,因此当参数仅作为输入时,每个指针或引用都必须使用const
,不是输出。const
仅在通过引用或指针传递的参数上使用具有额外的好处,可以使哪些参数是指针或引用变得非常明显。突出并说“小心!const
旁边的任何参数都是引用或指针!”是另一回事。(来自“ Google C++ 风格指南”)
对于按值传递的函数参数,const 对调用者没有影响,因此不建议在函数声明中使用。参见TotW #109。
既不鼓励也不鼓励在局部变量上使用 const。
来源:Google C++ 风格指南的“使用常量”部分:https : //google.github.io/styleguide/cppguide.html#Use_of_const。这实际上是一个非常有价值的部分,因此请阅读整个部分。
请注意,“TotW #109”代表“本周提示 #109:const
在函数声明中有意义”,也是一个有用的读物。它提供的信息更丰富,对做什么的规定更少,并且基于上下文出现在上面引用的 Google C++ 样式指南规则之前const
,但由于它提供的清晰性,const
上面引用的规则被添加到 Google C++时尚指南。
另请注意,即使我在这里引用 Google C++ 样式指南来捍卫我的立场,但这并不意味着我总是遵循该指南或始终建议遵循该指南。他们推荐的一些东西很奇怪,比如他们kDaysInAWeek
的 "Constant Names" 风格命名约定。然而,当世界上最成功和最有影响力的技术和软件公司之一使用与我和@Adisak 等其他人相同的理由来支持我们在这个问题上的观点时,指出这一点仍然是有用和相关的。
clang-tidy
, 对此有一些选择:A.这也是值得注意的是,铛的棉短绒,clang-tidy
,有一个选项,readability-avoid-const-params-in-decls
,这里描述的,以支持在一个代码库执行不使用const
对于传值函数的参数:
检查函数声明是否具有顶级常量的参数。
声明中的 const 值不会影响函数的签名,因此不应将它们放在那里。
例子:
Run Code Online (Sandbox Code Playgroud)void f(const string); // Bad: const is top level. void f(const 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]
Run Code Online (Sandbox Code Playgroud)
B. 它也有这个选项:readability-const-return-type
- https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html
我只需将其复制并粘贴到我的风格指南中:
[复制/粘贴开始]
const
当它们的内容(它们指向的内容)不打算更改时,始终在通过引用或指针传递的函数参数上使用。这样,当通过引用或指针传递的变量预计会被更改时,很明显,因为它将缺少const
. 在这个用例中,const
可以防止函数之外的意外副作用。const
上按值传递函数的参数,因为const
对来电者没有影响:即使变量是在函数变化会有以外的功能无副作用。有关其他理由和见解,请参阅以下资源:
const
[即:const
在按值传递的参数上] (并注意不要复制/粘贴无意义的)。它毫无意义并且被编译器忽略,它是视觉噪音,它可能会误导读者”(https://abseil.io/tips/109,重点补充)。const
const
对编译有影响的限定符是那些放置在函数定义中的限定符,而不是那些位于函数的前向声明中的限定符,例如头文件中的函数(方法)声明中的限定符。const
[即:const
在通过值传递的变量上] 。const
在函数返回的指针或引用上使用取决于实现者,因为它有时很有用。clang-tidy
选项强制执行上述某些操作:下面是一些代码示例来演示上述const
规则:
const
参数示例:(
有些是从这里借来的)
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]
Run Code Online (Sandbox Code Playgroud)
const
返回类型示例:(
有些是从这里借用的)
// 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();
Run Code Online (Sandbox Code Playgroud)
[复制/粘贴结束]
关键词:const
in 函数参数的使用;编码标准;C和C++编码标准;编码指南;最佳实践;代码标准;const 返回值
我在函数参数上使用const,这些参数是仅在[in]数据中的引用(或指针),并且不会被函数修改.意思是,当使用引用的目的是避免复制数据而不允许更改传递的参数时.
在示例中将const放在boolean b参数上只会对实现施加约束,并且不会为类的接口做出贡献(尽管通常建议不更改参数).
功能签名
void foo(int a);
Run Code Online (Sandbox Code Playgroud)
和
void foo(const int a);
Run Code Online (Sandbox Code Playgroud)
是相同的,这解释了你的.c和.h
阿萨夫
->*
或.*
运算符,这是必须的.它会阻止你写一些像
void foo(Bar *p) { if (++p->*member > 0) { ... } }
Run Code Online (Sandbox Code Playgroud)
我现在几乎做了,而且可能不会做你想要的.
我打算说的是
void foo(Bar *p) { if (++(p->*member) > 0) { ... } }
Run Code Online (Sandbox Code Playgroud)
如果我把const
之间的Bar *
和p
,编译器会告诉我的.
当参数按值传递时, const 毫无意义,因为您不会修改调用者的对象。
当通过引用传递时,const 应该是首选,除非函数的目的是修改传递的值。
最后,一个不修改当前对象(this)的函数可以并且可能应该声明为 const。一个例子如下:
int SomeClass::GetValue() const {return m_internalValue;}
Run Code Online (Sandbox Code Playgroud)
这是一个不修改应用此调用的对象的承诺。换句话说,您可以调用:
const SomeClass* pSomeClass;
pSomeClass->GetValue();
Run Code Online (Sandbox Code Playgroud)
如果函数不是 const,这将导致编译器警告。
标记值参数'const'绝对是一个主观的东西.
但是我实际上更喜欢将值参数标记为const,就像在您的示例中一样.
void func(const int n, const long l) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
我的值是清楚地表明功能参数值永远不会被函数改变.它们在开头和结尾处具有相同的值.对我来说,这是保持一种非常实用的编程风格的一部分.
对于一个简短的函数来说,在那里使用'const'可能是浪费时间/空间,因为通常非常明显的是参数不会被函数修改.
但是对于更大的函数,它是一种实现文档形式,并由编译器强制执行.
我可以肯定,如果我使用'n'和'l'进行一些计算,我可以重构/移动该计算而不用担心得到不同的结果,因为我错过了一个或两个都被改变的地方.
由于它是一个实现细节,因此您不需要在头文件中声明值参数const,就像您不需要使用与实现使用的名称相同的名称来声明函数参数.
归档时间: |
|
查看次数: |
235839 次 |
最近记录: |