在C ++中,通过指针传递比通过引用传递有什么好处?
最近,我看到了许多选择通过指针传递函数参数而不是通过引用传递函数的示例。这样做有好处吗?
例:
func(SPRITE *x);
打电话给
func(&mySprite);
与
func(SPRITE &x);
打电话给
func(mySprite);
在C ++中,通过指针传递比通过引用传递有什么好处?
最近,我看到了许多选择通过指针传递函数参数而不是通过引用传递函数的示例。这样做有好处吗?
例:
func(SPRITE *x);
打电话给
func(&mySprite);
与
func(SPRITE &x);
打电话给
func(mySprite);
Answers:
指针可以接收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);
func2 passes by reference
。我理解您的意思是从高层角度“通过引用”传递(通过在代码层角度传递指针来实现),这非常令人困惑(请参阅stackoverflow.com/questions/13382356/…)。
func(int& a)
在任何标准版本中都不是有效的C。您可能是无意间将文件编译为C ++。
nothing
。这可以用来提供可选参数。string s = &str1 + &str2;
使用指针。void f(const T& t); ... f(T(a, b, c));
:,因此不能像这样使用指针,因为您不能使用临时地址。我喜欢“ cplusplus.com”上一篇文章的推理:
当函数不想修改参数并且该值易于复制(int,double,char,bool等...简单类型。std :: string,std :: vector和所有其他STL)时,按值传递容器不是简单类型。)
当复制该值非常昂贵并且该函数不想修改指向的值时,通过const指针传递,并且NULL是该函数处理的有效预期值。
当复制的值昂贵且函数要修改指向的值时,通过非常量指针传递,并且NULL是函数处理的有效预期值。
当值复制成本很高并且函数不想修改引用的值时,通过const引用传递,如果使用指针代替,则NULL将不是有效值。
当值复制成本很高并且函数要修改引用的值时,通过非连续引用传递,并且如果使用指针代替,则NULL将不是有效值。
在编写模板函数时,没有明确的答案,因为有一些折衷考虑的问题超出了本次讨论的范围,但是可以说大多数模板函数都按值或(const)引用获取其参数。 ,但是,因为迭代器语法与指针的语法相似(星号为“取消引用”),所以任何以迭代器作为参数的模板函数在默认情况下也将接受指针(并且不检查NULL,因为NULL迭代器的概念具有不同的语法) )。
我从中得到的是选择使用指针或引用参数之间的主要区别是NULL是否是可接受的值。而已。
毕竟,该值的输入,输出,可修改等应在有关功能的文档/注释中。
艾伦·霍鲁布(Allen Holub)的《用绳子足以使自己脚下开枪》列出了以下两个规则:
120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers
他列出了为什么将引用添加到C ++的几个原因:
const
引用使您在避免复制的同时具有按值传递的语义他的主要观点是,不应将引用用作“输出”参数,因为在调用站点没有指示该参数是引用还是值参数的指示。因此,他的规则是仅将const
引用用作参数。
就我个人而言,我认为这是一个很好的经验法则,因为它可以使参数在何时成为输出参数变得更加清晰。但是,尽管我个人总体上同意这一点,但是如果他们争用输出参数作为参考,我确实会让自己受到团队中其他人的意见的影响(一些开发人员非常喜欢他们)。
对以上职位的说明:
引用不能保证获得非空指针。(尽管我们经常这样对待它们。)
在糟糕透顶的代码中(例如,将您带到错综复杂的坏代码后面),将编译并运行以下代码:(至少在我的编译器下)。
bool test( int & a)
{
return (&a) == (int *) NULL;
}
int
main()
{
int * i = (int *)NULL;
cout << ( test(*i) ) << endl;
};
我与参考文献之间的真正问题在于其他程序员,此后称为IDIOTS,他们在构造函数中分配,在析构函数中分配,并且无法提供副本构造函数或operator =()。
突然之间,foo(BAR bar)和foo(BAR & bar)之间存在天壤之别。(自动按位复制操作被调用。析构函数中的解除分配被调用两次。)
值得庆幸的是,现代编译器将采用相同指针的这种双重释放。15年前,他们没有。(在gcc / g ++下,使用setenv MALLOC_CHECK_0重新访问旧方法。)因此,在DEC UNIX下,同一内存分配给两个不同的对象。那里有很多调试乐趣...
实际上:
*i
,程序都具有未定义的行为。例如,编译器可以看到此代码并假定“好,此代码在所有代码路径中均具有未定义的行为,因此,整个函数必须不可访问。” 然后,将假定不采用导致该功能的所有分支。这是定期执行的优化。
就表达意图而言,此处的大多数答案都无法解决在函数签名中包含原始指针所固有的歧义。问题如下:
调用者不知道指针是指向单个对象还是指向对象“数组”的开头。
调用者不知道指针是否“拥有”它指向的内存。IE,该功能是否应释放内存。(foo(new int)
-这是内存泄漏吗?)。
调用者不知道是否nullptr
可以安全地传递到函数中。
所有这些问题都可以通过引用解决:
引用始终引用单个对象。
引用永远不会拥有它们所引用的内存,它们只是内存的视图。
引用不能为空。
这使参考成为更好的通用候选。但是,参考文献并不完美-需要考虑几个主要问题。
&
运算符来表明我们确实在传递指针。例如,int a = 5; foo(a);
在这里完全不清楚a是通过引用传递的并且可以修改。std::optional<T&>
无效(出于充分的理由),指针为我们提供了所需的可空性。如此看来,当我们想要带有显式间接的可为空的引用时,我们应该争取T*
权利吗?错误!
在对可为空性的绝望中,我们可能会寻求T*
,而只是忽略了前面列出的所有缺点和语义歧义。取而代之的是,我们应该追求C ++最擅长的:抽象。如果我们仅编写一个围绕指针包装的类,我们将获得表现力,可空性和显式间接性。
template <typename T>
struct optional_ref {
optional_ref() : ptr(nullptr) {}
optional_ref(T* t) : ptr(t) {}
optional_ref(std::nullptr_t) : ptr(nullptr) {}
T& get() const {
return *ptr;
}
explicit operator bool() const {
return bool(ptr);
}
private:
T* ptr;
};
这是我能想到的最简单的界面,但是它可以有效地完成工作。它允许初始化引用,检查值是否存在并访问该值。我们可以这样使用它:
void foo(optional_ref<int> x) {
if (x) {
auto y = x.get();
// use y here
}
}
int x = 5;
foo(&x); // explicit indirection here
foo(nullptr); // nullability
我们已经实现了目标!现在让我们看一下与原始指针相比的好处。
nullptr
可以传入,因为函数作者明确要求optional_ref
从这里开始,我们可以使接口更加复杂,例如添加相等运算符,monadic get_or
和map
接口,获取值或引发异常的方法,constexpr
支持。那可以由你完成。
总之,不要使用原始指针,而是要推理这些指针在代码中的实际含义,并利用标准库抽象或编写自己的库。这将大大改善您的代码。
并不是的。在内部,通过引用传递是通过实质上传递引用对象的地址来执行的。因此,通过指针实际上并没有任何效率上的提高。
但是,通过引用确实有一个好处。您可以确保拥有传入的任何对象/类型的实例。如果传入指针,则冒着接收到NULL指针的风险。通过使用按引用传递,您将隐式NULL检查向上推至函数的调用者。
new
创建一个指针以及随之而来的所有权问题。