在 C++ 中通过指针传递比通过引用传递有优势吗?
-
22-07-2019 - |
题
在 C++ 中通过指针传递比通过引用传递有什么好处?
最近,我看到许多选择通过指针传递函数参数而不是通过引用传递的示例。这样做有好处吗?
例子:
func(SPRITE *x);
打电话给
func(&mySprite);
与
func(SPRITE &x);
打电话给
func(mySprite);
解决方案
一个指针可以接收NULL参数,引用参数不能。如果有过,你可以想通过“无对象”的机会,然后用一个指针,而不是一个参考的。
另外,通过传递指针可以显式在调用位置看到该对象是否通过值或引用传递:
// Is mySprite passed by value or by reference? You can't tell
// without looking at the definition of func()
func(mySprite);
// func2 passes "by pointer" - no need to look up function definition
func2(&mySprite);
其他提示
通过指针传递
- 调用者必须获取地址 -> 不透明
- 可以提供 0 值来表示
nothing
. 。这可用于提供可选参数。
通过引用传递
- 调用者只需传递对象 -> 透明。必须用于运算符重载,因为指针类型的重载是不可能的(指针是内置类型)。所以你不能这样做
string s = &str1 + &str2;
使用指针。 - 不可能有 0 值 -> 被调用的函数不必检查它们
- 对 const 的引用也接受临时值:
void f(const T& t); ... f(T(a, b, c));
, ,指针不能像这样使用,因为你不能获取临时的地址。 - 最后但并非最不重要的一点是,引用更容易使用 -> 出现错误的机会更少。
艾伦·霍鲁布(Allen Holub)的《足够的绳子会搬起石头砸自己的脚》列出了以下两条规则:
120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers
他列出了 C++ 中添加引用的几个原因:
- 它们是定义复制构造函数所必需的
- 它们对于运算符重载是必要的
const
引用允许您拥有按值传递语义,同时避免复制
他的主要观点是引用不应该用作“输出”参数,因为在调用站点没有指示该参数是引用参数还是值参数。所以他的规则是只使用 const
引用作为参数。
就我个人而言,我认为这是一个很好的经验法则,因为它使得参数何时是输出参数更加清楚。然而,虽然我个人总体上同意这一点,但如果团队中其他人主张将输出参数作为参考,我确实会受到他们的意见的影响(一些开发人员非常喜欢它们)。
我喜欢“cplusplus.com”的一篇文章的推理:
当函数不想修改参数并且值易于复制时按值传递(ints、doubles、char、bool 等...简单类型。std::string、std::vector 和所有其他 STL 容器都不是简单类型。)
当复制该值的成本很高并且函数不想修改指向的值并且 NULL 是该函数处理的有效的预期值时,传递 const 指针。
当复制该值的成本很高并且函数想要修改指向的值并且 NULL 是函数处理的有效的预期值时,传递非常量指针。
当值的复制成本很高并且函数不想修改引用的值并且如果使用指针时 NULL 将不是有效值时,通过 const 引用传递。
当复制该值的成本昂贵并且函数想要修改引用的值并且如果使用指针时,NULL 将不是有效值时,传递非连续引用。
在编写模板函数时,没有明确的答案,因为需要考虑一些权衡,这些权衡超出了本讨论的范围,但足以说明大多数模板函数通过值或(常量)引用获取参数,但是,由于迭代器语法与指针类似(星号表示“取消引用”),因此任何需要迭代器作为参数的模板函数默认也会接受指针(并且不会检查 NULL,因为 NULL 迭代器概念具有不同的语法)。
我从中得出的结论是,选择使用指针或引用参数之间的主要区别在于 NULL 是否是可接受的值。就是这样。
值是否是输入、输出、可修改等。毕竟应该在有关该功能的文档/注释中。
对之前帖子的澄清:
参考文献是 不是 保证获得非空指针。(尽管我们经常这样对待它们。)
虽然代码非常糟糕,比如带你去木棚后面 坏的 代码,以下将编译并运行:(至少在我的编译器下是这样。)
bool test( int & a)
{
return (&a) == (int *) NULL;
}
int
main()
{
int * i = (int *)NULL;
cout << ( test(*i) ) << endl;
};
我在引用方面遇到的真正问题在于其他程序员,此后称为 白痴, ,谁在构造函数中分配,在析构函数中释放, 并且无法提供复制构造函数或运算符=()。
突然之间就有了天壤之别 foo(酒吧酒吧) 和 富(酒吧 & 酒吧). 。(自动按位复制操作被调用。析构函数中的释放被调用两次。)
值得庆幸的是,现代编译器将接受同一指针的双重释放。15年前,他们没有。(在 gcc/g++ 下,使用 setenv MALLOC_CHECK_ 0 重新访问旧方法。)导致在 DEC UNIX 下,同一内存被分配给两个不同的对象。那里有很多调试的乐趣......
更实际的是:
- 引用隐藏了您正在更改存储在其他地方的数据。
- 引用和复制对象很容易混淆。
- 指针一目了然!
不是真的。在内部,通过引用传递是由基本上通过引用的对象的地址执行的。所以,确实是不被传递指针可以了任何效率增益。
通过引用传递确实有一个好处,但是。你肯定有什么目标/是在传递类型的实例。如果你在一个指针传递,然后运行收到NULL指针的风险。通过使用传递的引用,你是推一个隐含的一个水平NULL检查到你的函数的调用者。