我了解指针和引用的语法和一般语义,但是我应该如何决定什么时候在API中使用引用或指针比较合适?

当然,有些情况需要其中一个(操作符++需要引用参数),但一般来说,我发现我更喜欢使用指针(和const指针),因为语法很清楚,变量是破坏性传递的。

例如,在以下代码中:

void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
  int a = 0;
  add_one(a); // Not clear that a may be modified
  add_one(&a); // 'a' is clearly being passed destructively
}

使用指针,它总是(更)明显的发生了什么,所以对于api和类似的地方,清晰度是一个大问题,指针不是比引用更合适吗?这是否意味着引用应该只在必要时使用(例如操作符++)?其中一种是否存在性能问题?

编辑(过时的):

除了允许NULL值和处理原始数组之外,选择似乎还取决于个人喜好。我接受下面的答案,引用谷歌的c++风格指南,因为他们提出的观点是“引用可能会令人困惑,因为它们有值语法,但有指针语义。”

由于需要额外的工作来清除不应该为NULL的指针参数(例如,add_one(0)将调用指针版本并在运行时中断),从可维护性的角度来看,在必须存在对象的地方使用引用是有意义的,尽管失去语法的清晰性是一种遗憾。


当前回答

免责声明:除了引用不能为NULL或“反弹”(意味着它们不能改变它们是别名的对象)这一事实之外,它真的归结为一个品味问题,所以我不会说“这更好”。

也就是说,我不同意你在文章中最后的说法,因为我不认为代码在引用中失去了清晰度。在你的例子中,

add_one(&a);

可能会更清楚

add_one(a);

因为你知道a的值很可能会改变。另一方面,函数的签名

void add_one(int* const n);

也不太清楚n是单个整数还是数组?有时您只能访问(文档记录不佳的)头文件和签名

foo(int* const a, int b);

乍一看不容易理解。

在我看来,当不需要(重新)分配或重新绑定(在前面解释的意义上)时,引用和指针一样好。此外,如果开发人员只对数组使用指针,那么函数签名就不会那么模糊。更不用说使用引用的操作符语法可读性更强了。

其他回答

我的经验法则是:

Use pointers for outgoing or in/out parameters. So it can be seen that the value is going to be changed. (You must use &) Use pointers if NULL parameter is acceptable value. (Make sure it's const if it's an incoming parameter) Use references for incoming parameter if it cannot be NULL and is not a primitive type (const T&). Use pointers or smart pointers when returning a newly created object. Use pointers or smart pointers as struct or class members instead of references. Use references for aliasing (eg. int &current = someArray[i])

无论您使用哪一种,如果参数不明显,请不要忘记记录函数及其参数的含义。

尽可能使用引用,必须使用指针。

避免指针,直到你不能。

原因是指针使事情更难跟踪/阅读,比任何其他结构更不安全,更危险的操作。

所以经验法则是只有在没有其他选择的情况下才使用指针。

例如,当函数在某些情况下可以返回nullptr时,返回指向对象的指针是一个有效的选项,并且假设它将会返回。也就是说,更好的选择是使用类似于std::optional(需要c++ 17;在此之前,有boost::optional)。

另一个例子是为特定的内存操作使用指向原始内存的指针。这应该隐藏并本地化在代码的非常狭窄的部分,以帮助限制整个代码库的危险部分。

在你的例子中,使用指针作为参数是没有意义的,因为:

如果你提供nullptr作为参数,你将进入未定义行为领域; 引用属性版本不允许(没有容易发现的技巧)1的问题。 对于用户来说,引用属性版本更容易理解:您必须提供一个有效的对象,而不是可以为空的东西。

如果函数的行为必须在有或没有给定对象的情况下工作,那么使用指针作为属性表明您可以将nullptr作为参数传递,这对函数来说是好的。这是用户和实现之间的一种契约。

把我的一角硬币放进去。我刚做了个测试。一个打喷嚏的人。我只是让g++使用指针而不是引用来创建同一个小程序的程序集文件。 当查看输出时,它们是完全相同的。除了符号命名。所以看性能(在一个简单的例子中)没有问题。

现在谈谈指针与引用的话题。恕我直言,我认为清晰是最重要的。一旦我读到内隐行为,我的脚趾就开始卷曲。我同意引用不能为NULL是很好的隐式行为。

解引用NULL指针不是问题所在。它将使您的应用程序崩溃,并且易于调试。一个更大的问题是未初始化的指针包含无效值。这很可能导致内存损坏,导致没有明确起源的未定义行为。

这就是我认为引用比指针安全得多的地方。我同意前面的说法,接口(应该清楚地记录,参见合同设计,Bertrand Meyer)定义了函数参数的结果。考虑到这些因素,我的偏好是 尽可能使用引用。

正确编写的示例应该如下所示

void add_one(int& n) { n += 1; }
void add_one(int* const n)
{
  if (n)
    *n += 1;
}

这就是为什么推荐信在可能的情况下更可取 ...

这不是品味的问题。以下是一些明确的规则。

如果你想在声明变量的范围内引用静态声明的变量,那么使用c++引用,这将是完全安全的。这同样适用于静态声明的智能指针。通过引用传递参数就是一个例子。

如果你想从一个比它声明的范围更宽的范围中引用任何东西,那么你应该使用一个引用计数智能指针,以确保它的完全安全。

为了语法方便,您可以使用引用引用集合的元素,但这并不安全;元素可以在任何时候被删除。

为了安全地保存对集合元素的引用,必须使用引用计数智能指针。