我了解指针与引用的语法和一般语义,但我应该如何决定何时在 API 中使用引用或指针或多或少合适?
当然,有些情况需要其中之一(operator++
需要一个引用参数),但总的来说,我发现我更喜欢使用指针(和 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 等,清晰度是一个大问题,指针不是比引用更合适吗?这是否意味着仅应在必要时使用引用(例如 operator++
)?其中之一是否存在性能问题?
编辑(已过时)
除了允许 NULL 值和处理原始数组之外,似乎选择取决于个人喜好。我已接受下面引用 Google 的 C++ 风格指南的答案,正如它们所呈现的那样认为“引用可能会令人困惑,因为它们具有值语法但具有指针语义。”。
由于清理不应该为 NULL 的指针参数需要额外的工作(例如 add_one(0)
将调用指针版本并在运行时中断),从可维护性的角度来看,在以下位置使用引用是有意义的:一个对象必须存在,尽管失去语法清晰度是一种耻辱。
I understand the syntax and general semantics of pointers versus references, but how should I decide when it is more-or-less appropriate to use references or pointers in an API?
Naturally some situations need one or the other (operator++
needs a reference argument), but in general I'm finding I prefer to use pointers (and const pointers) as the syntax is clear that the variables are being passed destructively.
E.g. in the following code:
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
}
With the pointer, it's always (more) obvious what's going on, so for APIs and the like where clarity is a big concern are pointers not more appropriate than references? Does that mean references should only be used when necessary (e.g. operator++
)? Are there any performance concerns with one or the other?
EDIT (OUTDATED)
Besides allowing NULL
values and dealing with raw arrays, it seems the choice comes down to personal preference. I've accepted the answer below that references Google's C++ Style Guide, as they present the view that "References can be confusing, as they have value syntax but pointer semantics.".
Due to the additional work required to sanitise pointer arguments that should not be NULL (e.g. add_one(0)
will call the pointer version and break during runtime), it makes sense from a maintainability perspective to use references where an object MUST be present, though it is a shame to lose the syntactic clarity.
发布评论
评论(17)
尽可能使用引用,并在必要时使用指针。
避免使用指针,除非你不能。
原因是,与任何其他构造相比,指针使事情更难以遵循/阅读、更不安全且危险得多。
因此,经验法则是只有在没有其他选择的情况下才使用指针。
例如,当函数在某些情况下可以返回 nullptr 并且假设它会返回时,返回指向对象的指针是一个有效的选项。也就是说,更好的选择是使用类似于
std::Optional< /code>
(需要 C++17;在此之前,有
boost::可选
)。另一个例子是使用指向原始内存的指针来进行特定的内存操作。它应该隐藏并本地化在代码的非常狭窄的部分,以帮助限制整个代码库的危险部分。
在您的示例中,使用指针作为参数是没有意义的,因为:
如果函数的行为必须在有或没有给定对象的情况下工作,那么使用指针作为属性建议您可以传递 nullptr 作为参数,这对于函数来说没问题。这是用户和实现之间的一种合同。
Use references wherever you can, and pointers wherever you must.
Avoid pointers until you can't.
The reason is that pointers make things harder to follow/read, less safe and far more dangerous manipulations than any other constructs.
So the rule of thumb is to use pointers only if there is no other choice.
For example, returning a pointer to an object is a valid option when the function can return
nullptr
in some cases and it is assumed it will. That said, a better option would be to use something similar tostd::optional
(requires C++17; before that, there'sboost::optional
).Another example is to use pointers to raw memory for specific memory manipulations. That should be hidden and localized in very narrow parts of the code, to help limit the dangerous parts of the whole code base.
In your example, there is no point in using a pointer as an argument because:
nullptr
as the argument, you're going in undefined-behaviour-land;If the behaviour of the function would have to work with or without a given object, then using a pointer as an attribute suggests that you can pass
nullptr
as the argument and it is fine for the function. That's kind of a contract between the user and the implementation.性能完全相同,因为引用在内部实现为指针。因此您无需担心这一点。
关于何时使用引用和指针,没有普遍接受的约定。在某些情况下,您必须返回或接受引用(例如复制构造函数),但除此之外,您可以自由地按照您的意愿进行操作。我遇到的一个相当常见的约定是,当参数必须引用现有对象时使用引用,而当 NULL 值可以时使用指针。
一些编码约定(例如 Google 的)规定应始终使用指针或 const 引用,因为引用有一些不清楚的语法:它们有引用行为但有值语法。
The performances are exactly the same, as references are implemented internally as pointers. Thus you do not need to worry about that.
There is no generally accepted convention regarding when to use references and pointers. In a few cases you have to return or accept references (copy constructor, for instance), but other than that you are free to do as you wish. A rather common convention I've encountered is to use references when the parameter must refer an existing object and pointers when a NULL value is ok.
Some coding convention (like Google's) prescribe that one should always use pointers, or const references, because references have a bit of unclear-syntax: they have reference behaviour but value syntax.
来自 C++ FAQ Lite -
From C++ FAQ Lite -
我的经验法则是:
&
)const
)const T&
),请使用传入参数的引用。int ¤t = someArray[i]
)无论您使用哪一种,如果函数及其参数的含义不明显,请不要忘记记录它们。
My rule of thumb is:
&
)const
if it's an incoming parameter)const T&
).int ¤t = someArray[i]
)Regardless which one you use, don't forget to document your functions and the meaning of their parameters if they are not obvious.
免责声明:除了引用不能为 NULL 也不能“反弹”(意味着不能更改它们的别名的对象)这一事实之外,这实际上取决于品味问题,所以我不会说“这样更好”。
也就是说,我不同意您在帖子中的最后一个声明,因为我认为代码不会因为引用而失去清晰度。在您的示例中,
可能会更清楚,
因为您知道 a 的值很可能会发生变化。但另一方面,函数的签名
也有些不清楚:n 是单个整数还是数组?有时您只能访问(文档记录很差)标头,并且诸如此类的签名
乍一看并不容易解释。
恕我直言,当不需要(重新)分配或重新绑定(在前面解释的意义上)时,引用与指针一样好。此外,如果开发人员仅使用数组指针,则函数签名就不那么模糊了。更不用说运算符语法通过引用更具可读性。
Disclaimer: other than the fact that references cannot be NULL nor "rebound" (meaning thay can't change the object they're the alias of), it really comes down to a matter of taste, so I'm not going to say "this is better".
That said, I disagree with your last statement in the post, in that I don't think the code loses clarity with references. In your example,
might be clearer than
since you know that most likely the value of a is going to change. On the other hand though, the signature of the function
is somewhat not clear either: is n going to be a single integer or an array? Sometimes you only have access to (poorly documentated) headers, and signatures like
are not easy to interpret at first sight.
Imho, references are as good as pointers when no (re)allocation nor rebinding (in the sense explained before) is needed. Moreover, if a developer only uses pointers for arrays, functions signatures are somewhat less ambiguous. Not to mention the fact that operators syntax is way more readable with references.
就像其他人已经回答的那样:始终使用引用,除非
NULL
/nullptr
的变量确实是有效状态。约翰·卡马克对此主题的观点是相似的:
http://www.altdevblogaday.com/2011/12/24/ static-code-analysis/
编辑 2012-03-13
用户 布雷特·库恩斯正确地评论:
确实如此,但即使用智能指针替换原始指针,问题仍然存在。
例如,
std::unique_ptr
和std::shared_ptr
都可以通过其默认构造函数构造为“空”指针:...意味着使用它们而不需要验证它们不为空可能会导致崩溃,这正是 J. Carmack 讨论的重点。
然后,我们遇到了一个有趣的问题“我们如何将智能指针作为函数参数传递?”
乔恩的答案 对于问题 C++ - 将引用传递给boost::shared_ptr,并且以下注释表明,即使如此,通过复制或通过引用传递智能指针并不像人们希望的那样清晰(我默认情况下喜欢“通过引用”,但是我可能是错的)。
Like others already answered: Always use references, unless the variable being
NULL
/nullptr
is really a valid state.John Carmack's viewpoint on the subject is similar:
http://www.altdevblogaday.com/2011/12/24/static-code-analysis/
Edit 2012-03-13
User Bret Kuhns rightly remarks:
True enough, but the question still remains, even when replacing raw pointers with smart pointers.
For example, both
std::unique_ptr
andstd::shared_ptr
can be constructed as "empty" pointers through their default constructor:... meaning that using them without verifying they are not empty risks a crash, which is exactly what J. Carmack's discussion is all about.
And then, we have the amusing problem of "how do we pass a smart pointer as a function parameter?"
Jon's answer for the question C++ - passing references to boost::shared_ptr, and the following comments show that even then, passing a smart pointer by copy or by reference is not as clear cut as one would like (I favor myself the "by-reference" by default, but I could be wrong).
这不是品味问题。以下是一些明确的规则。
如果您想在声明的范围内引用静态声明的变量,请使用 C++ 引用,这将是完全安全的。这同样适用于静态声明的智能指针。通过引用传递参数是这种用法的一个示例。
如果您想引用比声明范围更宽的范围内的任何内容,那么您应该使用引用计数智能指针,以确保它完全安全。
为了语法方便,您可以使用引用来引用集合中的元素,但这并不安全;该元素可以随时删除。
为了安全地保存对集合元素的引用,您必须使用引用计数智能指针。
It is not a matter of taste. Here are some definitive rules.
If you want to refer to a statically declared variable within the scope in which it was declared then use a C++ reference, and it will be perfectly safe. The same applies to a statically declared smart pointer. Passing parameters by reference is an example of this usage.
If you want to refer to anything from a scope that is wider than the scope in which it is declared then you should use a reference counted smart pointer for it to be perfectly safe.
You can refer to an element of a collection with a reference for syntactic convenience, but it is not safe; the element can be deleted at anytime.
To safely hold a reference to an element of a collection you must use a reference counted smart pointer.
“尽可能使用引用”规则存在问题,如果您想保留引用以供进一步使用,则会出现此问题。为了用示例来说明这一点,假设您有以下课程。
乍一看,在 RefPhone(const SimCard & card) 构造函数中通过引用传递参数似乎是个好主意,因为它可以防止将错误/空指针传递给构造函数。它以某种方式鼓励在堆栈上分配变量并从 RAII 中受益。
但随后,临时的事情就会摧毁你的幸福世界。
因此,如果您盲目地坚持引用,您就会牺牲传递无效指针的可能性来存储对已销毁对象的引用的可能性,这基本上具有相同的效果。
编辑:请注意,我坚持“尽可能使用引用,必须使用指针。除非不能,否则避免使用指针”。来自最受支持和接受的答案(其他答案也表明如此)。虽然这应该是显而易见的,但例子并不表明引用本身是不好的。然而,它们可能会被滥用,就像指针一样,它们可能会给代码带来威胁。
指针和引用之间存在以下差异。
本地 const引用 考虑到我目前的规则如下。
There is problem with "use references wherever possible" rule and it arises if you want to keep reference for further use. To illustrate this with example, imagine you have following classes.
At first it may seem to be a good idea to have parameter in
RefPhone(const SimCard & card)
constructor passed by a reference, because it prevents passing wrong/null pointers to the constructor. It somehow encourages allocation of variables on stack and taking benefits from RAII.But then temporaries come to destroy your happy world.
So if you blindly stick to references you trade off possibility of passing invalid pointers for the possibility of storing references to destroyed objects, which has basically same effect.
edit: Note that I sticked to the rule "Use reference wherever you can, pointers wherever you must. Avoid pointers until you can't." from the most upvoted and accepted answer (other answers also suggest so). Though it should be obvious, example is not to show that references as such are bad. They can be misused however, just like pointers and they can bring their own threats to the code.
There are following differences between pointers and references.
Taking those into account my current rules are as follows.
任何性能差异都非常小,以至于没有理由使用不太明确的方法。
首先,没有提到引用通常更优越的一种情况是 const 引用。对于非简单类型,传递
const 引用
可以避免创建临时变量,也不会导致您担心的混乱(因为该值没有被修改)。在这里,强迫一个人传递指针会导致您担心的混乱,因为看到地址被获取并传递给函数可能会让您认为值发生了变化。无论如何,我基本上同意你的观点。当函数正在做的事情不是很明显时,我不喜欢函数通过引用来修改它们的值。在这种情况下我也更喜欢使用指针。
当您需要返回复杂类型的值时,我倾向于更喜欢引用。例如:
在这里,函数名称清楚地表明您正在数组中获取信息。所以不存在混乱。
引用的主要优点是它们始终包含有效值,比指针更干净,并且支持多态性而不需要任何额外的语法。如果这些优点都不适用,则没有理由更喜欢引用而不是指针。
Any performance difference would be so small that it wouldn't justify using the approach that's less clear.
First, one case that wasn't mentioned where references are generally superior is
const
references. For non-simple types, passing aconst reference
avoids creating a temporary and doesn't cause the confusion you're concerned about (because the value isn't modified). Here, forcing a person to pass a pointer causes the very confusion you're worried about, as seeing the address taken and passed to a function might make you think the value changed.In any event, I basically agree with you. I don't like functions taking references to modify their value when it's not very obvious that this is what the function is doing. I too prefer to use pointers in that case.
When you need to return a value in a complex type, I tend to prefer references. For example:
Here, the function name makes it clear that you're getting information back in an array. So there's no confusion.
The main advantages of references are that they always contain a valid value, are cleaner than pointers, and support polymorphism without needing any extra syntax. If none of these advantages apply, there is no reason to prefer a reference over a pointer.
复制自 wiki-
我 100% 同意这一点,这就是为什么我认为只有在有充分理由时才应该使用参考文献。
Copied from wiki-
I agree 100% with this, and this is why I believe that you should only use a reference when you a have very good reason for doing so.
要记住的要点:
指针可以为
NULL
,引用不能为NULL
。引用更容易使用,当我们不想改变值而只需要函数中的引用时,可以使用
const
作为引用。指针与
*
一起使用,而引用与&
一起使用。当需要进行指针算术运算时使用指针。
您可以拥有指向 void 类型的指针
int a=5; void *p = &a;
但不能拥有对 void 类型的引用。指针与引用
判断何时使用什么
指针:用于数组、链接列表、树实现和指针算术。
参考:在函数参数和返回类型中。
Points to keep in mind:
Pointers can be
NULL
, references cannot beNULL
.References are easier to use,
const
can be used for a reference when we don't want to change value and just need a reference in a function.Pointer used with a
*
while references used with a&
.Use pointers when pointer arithmetic operation are required.
You can have pointers to a void type
int a=5; void *p = &a;
but cannot have a reference to a void type.Pointer Vs Reference
Verdict when to use what
Pointer: For array, linklist, tree implementations and pointer arithmetic.
Reference: In function parameters and return types.
以下是一些准则。
函数使用传递的数据而不对其进行修改:
如果数据对象很小,例如内置数据类型或小型结构,则按值传递。
如果数据对象是数组,请使用指针,因为这是您唯一的选择。使指针成为指向 const 的指针。
如果数据对象是一个大小合适的结构,请使用 const 指针或 const
提高程序效率的参考。您节省了所需的时间和空间
复制结构或类设计。使指针或引用常量。
如果数据对象是类对象,则使用const引用。类设计的语义常常需要使用引用,这也是C++添加的主要原因
此功能。因此,传递类对象参数的标准方法是通过引用。
函数修改调用函数中的数据:
1.如果数据对象是内置数据类型,则使用指针。如果你发现代码
就像fixit(&x)一样,其中x是一个int,很明显这个函数打算修改x。
2.如果数据对象是数组,则使用唯一的选择:指针。
3.如果数据对象是结构体,则使用引用或指针。
4.如果数据对象是类对象,则使用引用。
当然,这些只是指导方针,可能有不同的原因
选择。例如,cin 使用基本类型的引用,以便您可以使用 cin >> n
而不是 cin >> &n.
The following are some guidelines.
A function uses passed data without modifying it:
If the data object is small, such as a built-in data type or a small structure, pass it by value.
If the data object is an array, use a pointer because that’s your only choice. Make the pointer a pointer to const.
If the data object is a good-sized structure, use a const pointer or a const
reference to increase program efficiency.You save the time and space needed to
copy a structure or a class design. Make the pointer or reference const.
If the data object is a class object, use a const reference.The semantics of class design often require using a reference, which is the main reason C++ added
this feature.Thus, the standard way to pass class object arguments is by reference.
A function modifies data in the calling function:
1.If the data object is a built-in data type, use a pointer. If you spot code
like fixit(&x), where x is an int, it’s pretty clear that this function intends to modify x.
2.If the data object is an array, use your only choice: a pointer.
3.If the data object is a structure, use a reference or a pointer.
4.If the data object is a class object, use a reference.
Of course, these are just guidelines, and there might be reasons for making different
choices. For example, cin uses references for basic types so that you can use cin >> n
instead of cin >> &n.
您正确编写的示例应该类似于
这就是为什么如果可能的话,引用是更好的选择
...
Your properly written example should look like
That's why references are preferable if possible
...
参考文献更清晰、更易于使用,并且可以更好地隐藏信息。
但是,引用不能重新分配。
如果需要先指向一个对象,然后再指向另一个对象,则必须使用指针。引用不能为 null,因此如果存在相关对象可能为 null 的可能性,则不得使用引用。您必须使用指针。
如果您想自己处理对象操作,即如果您想在堆上而不是堆栈上为对象分配内存空间,则必须使用指针
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
在我的实践中,我个人确定了一个简单的规则 - 对可复制/可移动的原语和值使用引用,对具有长生命周期的对象使用指针。
对于 Node 示例,我肯定会使用
In my practice I personally settled down with one simple rule - Use references for primitives and values that are copyable/movable and pointers for objects with long life cycle.
For Node example I would definitely use
只是投入我的一角钱。我刚刚进行了测试。这是一个偷偷摸摸的人。与使用引用相比,我只是让 g++ 使用指针创建同一小程序的汇编文件。
当查看输出时,它们完全相同。除了符号命名之外。所以看看性能(在一个简单的例子中)是没有问题的。
现在讨论指针与引用的主题。恕我直言,我认为清晰度高于一切。一旦我读到隐性行为,我的脚趾就开始卷曲。我同意引用不能为 NULL 是很好的隐式行为。
取消引用 NULL 指针不是问题。它会让你的应用程序崩溃并且很容易调试。一个更大的问题是未初始化的指针包含无效值。这很可能会导致内存损坏,从而导致没有明确来源的未定义行为。
这就是我认为引用比指针安全得多的地方。我同意之前的说法,即接口(应该清楚地记录下来,请参阅合同设计,Bertrand Meyer)定义函数参数的结果。现在考虑到这一切我的偏好去
尽可能使用参考文献。
Just putting my dime in. I just performed a test. A sneeky one at that. I just let g++ create the assembly files of the same mini-program using pointers compared to using references.
When looking at the output they are exactly the same. Other than the symbolnaming. So looking at performance (in a simple example) there is no issue.
Now on the topic of pointers vs references. IMHO I think clearity stands above all. As soon as I read implicit behaviour my toes start to curl. I agree that it is nice implicit behaviour that a reference cannot be NULL.
Dereferencing a NULL pointer is not the problem. it will crash your application and will be easy to debug. A bigger problem is uninitialized pointers containing invalid values. This will most likely result in memory corruption causing undefined behaviour without a clear origin.
This is where I think references are much safer than pointers. And I agree with a previous statement, that the interface (which should be clearly documented, see design by contract, Bertrand Meyer) defines the result of the parameters to a function. Now taking this all into consideration my preferences go to
using references wherever/whenever possible.
对于指针,您需要它们指向某些内容,因此指针会占用内存空间。
例如,采用整数指针的函数将不会采用整数变量。因此,您需要首先创建一个指针以传递给函数。
至于参考,不会耗费内存。您有一个整型变量,可以将其作为引用变量传递。就是这样。您不需要专门为其创建引用变量。
For pointers, you need them to point to something, so pointers cost memory space.
For example a function that takes an integer pointer will not take the integer variable. So you will need to create a pointer for that first to pass on to the function.
As for a reference, it will not cost memory. You have an integer variable, and you can pass it as a reference variable. That's it. You don't need to create a reference variable specially for it.