我了解指针和引用的语法和一般语义,但是我应该如何决定什么时候在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
在这里,函数名清楚地表明您正在从数组中获取信息。这样就不会混淆了。
引用的主要优点是它们总是包含一个有效值,比指针更简洁,并且支持多态,而不需要任何额外的语法。如果这些优点都不适用,就没有理由更喜欢引用而不是指针。
任何性能差异都是如此之小,以至于使用不太清楚的方法都是不合理的。
首先,有一种引用通常更好的情况没有提到,那就是const引用。对于非简单类型,传递const引用可以避免创建临时类型,也不会引起您所关心的混乱(因为该值没有被修改)。在这里,强制某人传递指针会导致您所担心的混乱,因为看到地址被获取并传递给函数可能会使您认为值已更改。
无论如何,我基本上同意你的看法。我不喜欢函数接受引用来修改它们的值,而这不是很明显的函数在做什么。在这种情况下,我也更喜欢使用指针。
当需要以复杂类型返回值时,我倾向于使用引用。例如:
bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative
在这里,函数名清楚地表明您正在从数组中获取信息。这样就不会混淆了。
引用的主要优点是它们总是包含一个有效值,比指针更简洁,并且支持多态,而不需要任何额外的语法。如果这些优点都不适用,就没有理由更喜欢引用而不是指针。
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
“尽可能使用参考文献”规则有问题,如果你想保留参考文献供进一步使用,就会出现这种问题。为了举例说明这一点,假设您有以下类。
class SimCard
{
public:
explicit SimCard(int id):
m_id(id)
{
}
int getId() const
{
return m_id;
}
private:
int m_id;
};
class RefPhone
{
public:
explicit RefPhone(const SimCard & card):
m_card(card)
{
}
int getSimId()
{
return m_card.getId();
}
private:
const SimCard & m_card;
};
乍一看,通过引用传递RefPhone(const SimCard & card)构造函数中的形参似乎是个好主意,因为它可以防止向构造函数传递错误/空指针。它以某种方式鼓励在堆栈上分配变量,并从RAII中获益。
PtrPhone nullPhone(0); //this will not happen that easily
SimCard * cardPtr = new SimCard(666); //evil pointer
delete cardPtr; //muahaha
PtrPhone uninitPhone(cardPtr); //this will not happen that easily
但是暂时的事情会摧毁你的幸福世界。
RefPhone tempPhone(SimCard(666)); //evil temporary
//function referring to destroyed object
tempPhone.getSimId(); //this can happen
因此,如果你盲目地坚持引用,你就在传递无效指针的可能性与存储已销毁对象引用的可能性之间进行了权衡,这基本上是相同的效果。
edit:请注意,我坚持了这条规则:“尽可能使用引用,必须使用指针。”避免指针,直到你做不到为止。”这是被点赞最多、接受度最高的答案(其他答案也这么认为)。虽然这应该是显而易见的,但例子并不表明引用是不好的。然而,它们也可能被滥用,就像指针一样,它们也会给代码带来威胁。
指针和引用之间有以下区别。
当涉及到传递变量时,按引用传递看起来像按值传递,但具有指针语义(充当指针)。
引用不能直接初始化为0 (null)。
引用(引用,未引用对象)不能修改(相当于“* const”指针)。
Const引用可以接受临时形参。
局部const引用延长了临时对象的生存期
考虑到这些因素,我目前的规则如下。
Use references for parameters that will be used locally within a function scope.
Use pointers when 0 (null) is acceptable parameter value or you need to store parameter for further use. If 0 (null) is acceptable I am adding "_n" suffix to parameter, use guarded pointer (like QPointer in Qt) or just document it. You can also use smart pointers. You have to be even more careful with shared pointers than with normal pointers (otherwise you can end up with by design memory leaks and responsibility mess).