我了解指针和引用的语法和一般语义,但是我应该如何决定什么时候在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)将调用指针版本并在运行时中断),从可维护性的角度来看,在必须存在对象的地方使用引用是有意义的,尽管失去语法的清晰性是一种遗憾。
任何性能差异都是如此之小,以至于使用不太清楚的方法都是不合理的。
首先,有一种引用通常更好的情况没有提到,那就是const引用。对于非简单类型,传递const引用可以避免创建临时类型,也不会引起您所关心的混乱(因为该值没有被修改)。在这里,强制某人传递指针会导致您所担心的混乱,因为看到地址被获取并传递给函数可能会使您认为值已更改。
无论如何,我基本上同意你的看法。我不喜欢函数接受引用来修改它们的值,而这不是很明显的函数在做什么。在这种情况下,我也更喜欢使用指针。
当需要以复杂类型返回值时,我倾向于使用引用。例如:
bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative
在这里,函数名清楚地表明您正在从数组中获取信息。这样就不会混淆了。
引用的主要优点是它们总是包含一个有效值,比指针更简洁,并且支持多态,而不需要任何额外的语法。如果这些优点都不适用,就没有理由更喜欢引用而不是指针。
尽可能使用引用,必须使用指针。
避免指针,直到你不能。
原因是指针使事情更难跟踪/阅读,比任何其他结构更不安全,更危险的操作。
所以经验法则是只有在没有其他选择的情况下才使用指针。
例如,当函数在某些情况下可以返回nullptr时,返回指向对象的指针是一个有效的选项,并且假设它将会返回。也就是说,更好的选择是使用类似于std::optional(需要c++ 17;在此之前,有boost::optional)。
另一个例子是为特定的内存操作使用指向原始内存的指针。这应该隐藏并本地化在代码的非常狭窄的部分,以帮助限制整个代码库的危险部分。
在你的例子中,使用指针作为参数是没有意义的,因为:
如果你提供nullptr作为参数,你将进入未定义行为领域;
引用属性版本不允许(没有容易发现的技巧)1的问题。
对于用户来说,引用属性版本更容易理解:您必须提供一个有效的对象,而不是可以为空的东西。
如果函数的行为必须在有或没有给定对象的情况下工作,那么使用指针作为属性表明您可以将nullptr作为参数传递,这对函数来说是好的。这是用户和实现之间的一种契约。