Mar*_*ork 39 c++ pointers coding-style reference
我加入了一个新的小组,其编码指南(对我而言)似乎过时了.
但只是在没有有效备份的情况下反对机器不会让我无处可去.
所以我转向SO,看看我们是否能够理性的理由支持/反对(嘿,我的选择可能是错的,所以我们会赞赏论证的双方).
争论的准则是:
提示:对返回参数使用指针而不是引用.
Run Code Online (Sandbox Code Playgroud)void Func1( CFoo &Return ); // bad void Func2( CFoo *pReturn ); // good理由:
使用引用时,它看起来与值相同.调用该函数后,调用者可能会惊讶于他的值已被更改.被调用者可能会无意中修改该值而不会影响调用者的值.通过使用指针,调用者和被调用者都清楚可以更改该值.在代码审查中使用引用可能特别容易引起误解.
Jam*_*lis 35
使用引用时,它看起来与值相同.
只有你真的没有注意到你在做什么.好的,有时会发生这种情况,但实际上......没有多少编码标准可以纠正那些不注意或不知道自己在做什么的人.
调用该函数后,调用者可能会惊讶于他的值已被更改.
如果您对调用函数时发生的情况感到惊讶,那么该函数的记录很少.
给定一个函数的名称,它的参数列表,以及一些非常简短和描述性的文档,它应该非常清楚函数的作用以及它的可观察副作用是什么(包括是否修改了任何参数).
被调用者可能会无意中修改该值而不会影响调用者的值.
如果函数是const正确的,那么这不是问题.如果函数不是const正确的话,那么它应该是const正确的,如果可以的话(追溯性地使代码const正确可以是绝对的跳动).
但是,这个基本原理没有多大意义:当您实际编写函数的代码时,您应该能够看到参数的声明.如果函数太长而你不能,则需要重构.
通过使用指针,调用者和被调用者都清楚可以更改该值.
这不完全正确.函数可以获取指向const对象的指针,在这种情况下,无法更改对象.
在代码审查中使用引用可能特别容易引起误解.
只有进行代码审查的人不知道他们在做什么.
所有这一切都很好,但为什么应该使用pass-by-reference而不是pass-by-pointer?最明显的原因是引用不能为空.
在一个带指针的函数中,你必须在使用之前检查指针是否为空,至少在调试断言时是这样.在正确的代码审查期间,您必须分析更多代码,以确保不会意外地将空指针传递给不期望的函数.我发现由于这个原因,审查带有指针参数的函数需要更长的时间; 使用指针时更容易弄错.
Gre*_*ill 18
在我看来,正确使用const将(大部分)消除对该提示的需要.仍然看起来有用的部分是在阅读调用者代码时,看到:
Func1(x);
Run Code Online (Sandbox Code Playgroud)
目前尚不清楚正在做什么x(特别是像一个不起眼的名字一样Func1).而是使用:
Func2(&x);
Run Code Online (Sandbox Code Playgroud)
使用上述约定,向调用者指示他们应该期望x被修改.
Pet*_*der 11
虽然我不会用尖端的忠告我自己,理由是有效的,这就是为什么像C#语言介绍了out和ref关键字在调用现场使用.
我可以提出的最好的论据是:不要求人们使用指针,而应该要求人们编写反映函数功能的函数名称.当我打电话时std::swap,我知道它会改变参数的值,因为名字暗示了这一点.另一方面,如果我要调用一个函数getSize,我不希望修改任何参数.
And*_*mas 10
如果你还没有,请购买一份Herb Sutter和Andrei Alexandrescu的"C++编码标准:101规则,指南和最佳实践".阅读.推荐给你的同事.它是本地编码风格的良好基础.
在第25条中,作者建议:
"如果需要参数,则首选通过引用传递,函数不会存储指向它的指针或以其他方式影响其所有权.这表明该参数是必需的,并使调用者负责提供有效的对象."
"Argument is required"表示NULL不是有效值.
缺陷的最常见原因之一是空指针的意外取消引用.在这些情况下使用引用而不是指针可以在编译时消除这些.
因此,您需要权衡 - 消除频繁的错误来源,或通过函数名以外的方式确保调用代码的可理解性.我个人倾向于消除风险.
编码标准基于习惯和常识.你的一些同事可能依赖于多年来根深蒂固的假设,即指针未传递的参数不会改变 - 对它们表示同情.
编码标准的重要部分并不是它们是最优的,而是每个人都遵守它们,以便与代码体保持一致.
小智 7
如果他们真的想要在呼叫站点明确提到输出参数,他们实际上应该要求而不是通过尝试使指针表示他们没有的东西来攻击它.指针不仅仅意味着修改比引用更多,并且为非修改对象传递指针并不罕见.
明确表达参数的一种可能方式:
template<class T>
struct Out {
explicit Out(T& obj) : base(obj) {}
T& operator*() { return base; }
T* operator->() { return &base; }
private:
T& base;
};
template<class T>
Out<T> out(T& obj) {
return Out<T>(obj);
}
void f(Out<int> n) {
++*n;
}
int main() {
int n = 3;
f(out(n));
cout << n << '\n';
}
Run Code Online (Sandbox Code Playgroud)
作为临时措施,直到他们将旧代码更改为此,您可以将Out convertible转换为指针和/或引用:
// in class definition
operator T*() { return &base; }
operator T&() { return base; }
// elsewhere
void old(int *p);
void g() {
int n;
old(out(n));
}
Run Code Online (Sandbox Code Playgroud)
我继续编写了这个所需的各种类,以及in-out参数,以一种应该很好地降级的方式.我怀疑我很快就会使用这个约定(至少在C++中),但它适用于任何想要使调用站点显式化的人.
| 归档时间: |
|
| 查看次数: |
53140 次 |
| 最近记录: |