我了解指针和引用的语法和一般语义,但是我应该如何决定什么时候在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/nullptr是一个真正有效的状态。
约翰·卡马克在这个问题上的观点也类似:
空指针是C/ c++中最大的问题,至少在我们的代码中是这样。将一个值同时用作标志和地址会导致大量致命问题。在任何可能的情况下,c++引用应该比指针更受欢迎;虽然引用“实际上”只是一个指针,但它有一个非null的隐含契约。当指针转换为引用时执行NULL检查,然后可以忽略此问题。
http://www.altdevblogaday.com/2011/12/24/static-code-analysis/
编辑2012-03-13
用户Bret Kuhns正确地评论道:
c++ 11标准已经完成。我认为是时候在这个线程中提到,大多数代码应该可以很好地使用引用、shared_ptr和unique_ptr的组合。
确实如此,但是问题仍然存在,即使用智能指针替换原始指针也是如此。
例如,std::unique_ptr和std::shared_ptr都可以通过它们的默认构造函数构造为“空”指针:
http://en.cppreference.com/w/cpp/memory/unique_ptr/unique_ptr
http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
... 这意味着在不验证它们是否为空的情况下使用它们会有崩溃的风险,这正是J. Carmack讨论的全部内容。
然后,我们有一个有趣的问题:“如何将智能指针作为函数参数传递?”
Jon对这个问题的回答是c++——将引用传递给boost::shared_ptr,下面的注释表明,即使这样,通过复制或引用传递智能指针也不像人们想的那样明确(默认情况下我倾向于使用“通过引用”,但我可能错了)。
References are cleaner and easier to use, and they do a better job of hiding information.
References cannot be reassigned, however.
If you need to point first to one object and then to another, you must use a pointer. References cannot be null, so if any chance exists that the object in question might be null, you must not use a reference. You must use a pointer.
If you want to handle object manipulation on your own i.e if you want to allocate memory space for an object on the Heap rather on the Stack you must use Pointer
int *pInt = new int; // allocates *pInt on the Heap
任何性能差异都是如此之小,以至于使用不太清楚的方法都是不合理的。
首先,有一种引用通常更好的情况没有提到,那就是const引用。对于非简单类型,传递const引用可以避免创建临时类型,也不会引起您所关心的混乱(因为该值没有被修改)。在这里,强制某人传递指针会导致您所担心的混乱,因为看到地址被获取并传递给函数可能会使您认为值已更改。
无论如何,我基本上同意你的看法。我不喜欢函数接受引用来修改它们的值,而这不是很明显的函数在做什么。在这种情况下,我也更喜欢使用指针。
当需要以复杂类型返回值时,我倾向于使用引用。例如:
bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative
在这里,函数名清楚地表明您正在从数组中获取信息。这样就不会混淆了。
引用的主要优点是它们总是包含一个有效值,比指针更简洁,并且支持多态,而不需要任何额外的语法。如果这些优点都不适用,就没有理由更喜欢引用而不是指针。
把我的一角硬币放进去。我刚做了个测试。一个打喷嚏的人。我只是让g++使用指针而不是引用来创建同一个小程序的程序集文件。
当查看输出时,它们是完全相同的。除了符号命名。所以看性能(在一个简单的例子中)没有问题。
现在谈谈指针与引用的话题。恕我直言,我认为清晰是最重要的。一旦我读到内隐行为,我的脚趾就开始卷曲。我同意引用不能为NULL是很好的隐式行为。
解引用NULL指针不是问题所在。它将使您的应用程序崩溃,并且易于调试。一个更大的问题是未初始化的指针包含无效值。这很可能导致内存损坏,导致没有明确起源的未定义行为。
这就是我认为引用比指针安全得多的地方。我同意前面的说法,接口(应该清楚地记录,参见合同设计,Bertrand Meyer)定义了函数参数的结果。考虑到这些因素,我的偏好是
尽可能使用引用。