值参数的 const 正确性
Posted
技术标签:
【中文标题】值参数的 const 正确性【英文标题】:Const correctness for value parameters 【发布时间】:2010-12-15 23:51:08 【问题描述】:我知道很少有关于 const 正确性的问题,其中声明函数的声明及其定义不需要就值参数达成一致。这是因为值参数的常量只在函数内部很重要。这很好:
// header
int func(int i);
// cpp
int func(const int i)
return i;
这样做真的是最佳做法吗?因为我从未见过有人这样做。我在其他地方看到过这个引用(不确定来源),这已经被讨论过:
“事实上,对于编译器来说,无论你在值参数前是否包含这个const,函数签名都是一样的。”
"避免在函数声明中使用 const 传值参数。如果不修改,仍将参数 const 放在同一个函数的定义中。"
第二段说不要将 const 放在声明中。我认为这是因为值参数的常量性作为接口定义的一部分是没有意义的。这是一个实现细节。
基于这个推荐,指针参数的指针值是否也推荐? (它对引用参数没有意义,因为您不能重新分配引用。)
// header
int func1(int* i);
int func2(int* i);
// cpp
int func1(int* i)
int x = 0;
*i = 3; // compiles without error
i = &x; // compiles without error
return *i;
int func2(int* const i)
int x = 0;
*i = 3; // compiles without error
i = &x; // compile error
return *i;
总结:制作值参数对于捕捉一些逻辑错误很有用。这是最佳实践吗?您是否走到了将 const 排除在头文件之外的极端? const 指针值是否同样有用?为什么或为什么不?
一些参考资料:
C++ const keyword - use liberally? Use of 'const' for function parameters
const 值参数何时有用的示例:
bool are_ints_equal(const int i, const int j)
if (i = j) // without the consts this would compile without error
return true;
else
return false;
// return i = j; // I know it can be shortened
【问题讨论】:
在这种情况下它会捕获“if (i = j)”错误,但不会捕获所有此类错误,所以我不会对那个特定的理由感到太兴奋(因为你可以与变量相同的错误)。即使没有 const,如果你告诉它你想要警告,你的编译器也应该警告你。 对if (i = j)
感到兴奋的一点是要意识到 const 值参数不仅仅是绒毛。 Michael Burr 的例子比这个更好。
在您不更改函数参数的情况下,您应该使用 const 参数,因为 A)它更安全,B)它是自文档化的,C)它对调试更友好。此外,原型和标头应标记为 const。如果只在函数头中完成,那就令人困惑了。关于在函数中创建临时变量的论点是您可能不需要声明参数 const 的情况。这是我的 2 便士。
【参考方案1】:
我读过很多次,在函数中设置值参数是一件坏事,因为它是不必要的。
但是,我发现它有时对我很有帮助,因为它可以检查我的实现没有做我不打算做的事情(如您问题末尾的示例)。
因此,虽然它可能不会为调用者增加价值,但它有时会为我作为实施者增加一点价值,并且不会从调用者那里拿走任何东西。所以我认为使用它并没有什么坏处。
例如,我可能正在实现一个 C 函数,它接受几个指向缓冲区的指针——一个指向开始的指针和一个指向结束的指针。我打算将数据放入缓冲区,但要确保不会超出末尾。所以在函数内部有代码会在我向它添加数据时增加一个指针。将指向缓冲区末尾的指针设置为const
参数将确保我不会编写一个错误,该错误会意外地增加结束边界指针而不是我真正应该增加的指针。
所以一个带有这样签名的 fillArray 函数:
size_t fillArray( data_t* pStart, data_t* const pEnd);
将防止我在我真正想要增加 pStart
时意外增加 pEnd
。这不是什么大问题,但我很确定每个用过 C 语言编程过一段时间的人都遇到过这样的错误。
【讨论】:
优秀。我正在寻找一个 const 指针值的好例子。【参考方案2】:我的看法:
这不是一个坏主意,但问题很小,你的精力可能会更好地花在其他事情上。
在您的问题中,您提供了一个很好的示例,说明它何时可能会捕获错误,但有时您最终也会这样做:
void foo(const int count /* … */)
int temp = count; // can't modify count, so we need a copy of it
++temp;
/* … */
无论哪种方式,利弊都是次要的。
【讨论】:
+1 表示这不是一个坏主意,并指出问题很小。但是在你复制参数之前,我会删除 const(除非你总是复制“count”,作为不对参数进行操作的原则,而不仅仅是因为 const 阻止了你) 通常当你像这样修改变量时,你正在改变语义。例如: void foo(const int index0based)const int index1based = index0based + 1; 您可以将其称为 index 并更改它,但这对于 5 年后出现的可怜的维护程序员来说要清楚得多。 Bill- 我的例子是人为的,但在很多情况下你可能是对的。维护程序员的问题对我来说很重要;) 实际上,我已经看到了与您的示例类似的代码。传入项目时的计数,而不是创建本地int currentCount = count;
,而是在函数执行其业务时使用和调整参数......非常混乱!【参考方案3】:
不幸的是,一些编译器(我在看着你,Sun CC!)错误地区分声明为 const 的参数和未声明为 const 的参数,您可能会收到有关未定义函数的错误。
【讨论】:
【参考方案4】:我喜欢这种情况下的 const 正确性:void foo(const Bar &b) //I know b cannot be changed
//do something with b
这让我可以使用b
而不必担心修改它,而且我不必支付复制构造函数的成本。
【讨论】:
我们不是在谈论参考参数。仅限值参数。【参考方案5】:我认为这取决于你的个人风格。
它不会增加或减少客户端可以传递给您的函数的内容。本质上,它就像一个编译时断言。如果它可以帮助您知道价值不会改变,那就去做吧,但我认为其他人没有很大的理由这样做。
我可能不这样做的一个原因是 value 参数的 const-ness 是您的客户不需要知道的实现细节。如果您稍后(故意)更改您的函数以使其确实更改了该值,您将需要更改函数的签名,这将迫使您的客户端重新编译。
这类似于为什么有些人建议不要使用公共虚拟方法(函数虚拟性是一个应该对客户端隐藏的实现细节),但我不属于那个特定的阵营。
【讨论】:
重读问题。头文件没有 const ,因为正如您所说,调用者不关心实现是否修改了局部变量。严格来说,这是一个关于在实施方面是否值得的问题。【参考方案6】:如果存在 const 关键字;这意味着 'i' 的值(它是 const 类型)不能被修改。 如果 'i' 的值在 foo 函数内发生变化,编译器会抛出错误:"
不能修改 const 对象
但是改变'*i'(即*i = 3;)意味着你没有改变'i'的值,而是'i'指向的地址的值
其实const函数适用于不应该被函数改变的大对象。
【讨论】:
以上是关于值参数的 const 正确性的主要内容,如果未能解决你的问题,请参考以下文章