假设您有一个修改变量的函数。
你应该这样写:void myfunc(int *a)
还是像这样void myfunc(int &a)
?
前者强制您使用myfunc(&b)
调用该函数,以便调用者知道b
将被修改,但后者更短,可以使用myfunc(b)
简单调用。那么哪个更好用?还有其他我想念的东西吗?
答案 0 :(得分:39)
指针(即'*')应该在传递“NULL”有意义的地方使用。 例如,您可以使用NULL来表示需要创建特定对象,或者不需要执行特定操作。 或者是否需要从非C ++代码调用它。 (例如,在共享库中使用)
例如。 libc函数time_t time (time_t *result);
如果result
不为NULL,则将存储当前时间。但是如果result
为NULL,则不执行任何操作。
如果您正在编写的函数不需要使用NULL作为有意义的值,那么使用引用(即'&')可能会减少混淆 - 假设这是您的项目使用的约定。
答案 1 :(得分:12)
我尽可能使用引用而不是指针。这样做的原因是,搞砸引用比指针更难。人们总是可以将NULL传递给指针值,但是没有这样的等价于引用。
唯一真正的缺点是C ++中的参考参数缺少呼叫站点文档。有些人认为这使得理解代码变得更加困难(我同意一定程度)。我通常在我的代码中定义以下内容并将其用于虚假的呼叫站点文档
#define byref
...
someFunc(byref x);
这当然不会强制执行呼叫站点文档。它只是提供了一种记录它的非常蹩脚的方式。我用模板进行了一些实验,该模板强制执行调用站点文档。尽管如此,这比实际生产代码更有趣。
http://blogs.msdn.com/jaredpar/archive/2008/04/03/reference-values-in-c.aspx
答案 2 :(得分:7)
我想我会不同意@bb和@JaredPar,我倾向于围栏的另一面。经过多年努力支持其他人的C ++代码,我经常发现潜在的参考参数的非明显副作用的问题。使用C#,很明显,因为你必须使用'ref'/'out'为参数类型添加前缀,但引用在C ++中可能会引起混淆。所以,我喜欢指针,因为很明显有些东西会重新出现。如果你不喜欢积分,那么C ++不适合你。
答案 3 :(得分:6)
由于这里和其他地方引用的原因,我来到围栏的指针一侧。但是,我会说,无论你决定什么,你都需要保持一致,并在你的风格指南中记录它。
Google C ++样式指南bans非const引用参数。
答案 4 :(得分:1)
前者迫使你打电话给 函数与myfunc(& b)一起调用 知道b将被修改
有时函数可以接受const指针,调用者会错误地认为b将被修改。
我的建议 - 更喜欢在任何可能的地方使用参考资料(当然需要它)。在功能参数的情况下 - 我们得到好处:
- 引用不能为NULL - 它有助于我们避免错误和不必要的断言或检查
- 引用只有一个初始化点,在函数boody中你总是知道输入参数指向什么东西。
我是大型项目的维护者。在任何一种情况下,我都会在调用它之前查看函数定义。当我查看函数定义时,我通过值,引用,const引用或指针看到参数定义。
但这似乎是圣战问题,不同的人在这一点上有不同的看法。例如。 google codding convension推荐在参数中使用指针,这些指针可以更改并且只允许const引用: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments
所有通过引用传递的参数 必须标记为const。
答案 5 :(得分:1)
如果NULL没有意义,我喜欢通过引用传递,但我可以看到两者的参数。如果您对编码非常小心,可以通过确保始终通过const引用传递变量来消除意外的参考传递异议,例如:
myfunc( const_cast< const int& >( a ) );
// Alternatively, this approach may require additional handling
// in the function, but it's cleaner at call point
myfunc( boost::cref( a ) );
但是这很多额外的代码几乎没有什么好处。正如Kenny指出的那样,C#从另一端解决了这个问题(需要通过引用进行特定的传递),但这不是C ++的一个选项(例如,除非您编写了函数以将引用包装器作为参数,例如boost :: ref(param)),例如:
void myfunc( const boost::reference_wrapper< int >& a ) { ... }
修复指针问题更有问题,但是......没有编译时方法来确保指针有效,因此最终会出现指针问题的运行时问题或运行时检查,或两者兼而有之。这是指针的本质。
无论如何,这只是我的意见,因为它的价值。
答案 6 :(得分:1)
需要注意的是,如果您使用的是stl仿函数,则参数与容器值类型匹配会更容易。
void foo(Bar *);
void frobnicate(vector<Bar *> vecBars)
{
for_each(vecBars.begin(),
vecBars.end(),
ptr_fun(&foo));
}
如果foo采用Bar&amp;
,上面的代码会更难答案 7 :(得分:0)
在理想的世界中,它取决于参数是否是可选输出。如果他们不关心它,你想让调用者传递NULL吗?然后使用指针。否则,参考是更好的选择。
请注意,在这两种情况下,语言都会使记录输出参数变得很麻烦。如果整个代码库是const正确的话会更好,那么用户可以假设任何非const引用或指针参数都是输出。
答案 8 :(得分:0)
之前提到的一个区别是,您无法传递空引用,但可以传递空指针。
另一件事,也是已经提到的,当你致电f(a,b)
时,如果来电者不知道f
可能会改变b
然而,另一个相当微妙的问题,但I still ran into it,是引用的语义。
指针按值传递,但引用不是。
这意味着,如果您通过指针传递参数,则可以更改指针并使其指向其他内容。
考虑一下:
void f1_ptr( type * a )
{
a = new type(); //no change to passed parameters, you're changing the pointer which was passed by value
}
void f2_ptr( type * a )
{
*a = some_other_value; //now you're changing the value of the parameter that was passed
//or, if type is a class or struct:
a->some_method_that_modifies_object(); //again, changing the parameter that was passed
}
但是,在通过引用传递时,您无法更改引用以引用其他值。设置参考后,就无法更改。
void f3_ref( type& a )
{
a = type(); //the referred variable has also changed
}
//....
type obj = type( params );
f3_ref( obj ); //obj now changed
f1_ptr( &obj ); //obj doesn't change
f2_ptr( &obj ); //obj now changed