Mat*_*coe 220 c++ pointers parameter-passing pass-by-reference
在C++中通过引用传递指针有什么好处?
最近,我看到一些例子选择通过指针传递函数参数而不是通过引用传递.这样做有好处吗?
例:
func(SPRITE *x);
Run Code Online (Sandbox Code Playgroud)
随叫随到
func(&mySprite);
Run Code Online (Sandbox Code Playgroud)
与
func(SPRITE &x);
Run Code Online (Sandbox Code Playgroud)
随叫随到
func(mySprite);
Run Code Online (Sandbox Code Playgroud)
Joh*_*itb 236
nothing.这可用于提供可选参数.string s = &str1 + &str2;使用指针.void f(const T& t); ... f(T(a, b, c));指针不能像那样使用,因为你不能使用临时的地址.Ada*_*eld 212
一个指针可以接收一个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);
Run Code Online (Sandbox Code Playgroud)
Mic*_*urr 62
艾伦·霍鲁布的"足够的足球射门"列出了以下两条规则:
120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers
Run Code Online (Sandbox Code Playgroud)
他列举了为什么将引用添加到C++的几个原因:
const 引用允许您在避免复制的同时具有按值传递的语义他的主要观点是参考不应该用作"输出"参数,因为在呼叫站点没有指示参数是参考还是值参数.所以他的规则是只使用const引用作为参数.
就个人而言,我认为这是一个很好的经验法则,因为当参数是输出参数时它会更清楚.然而,虽然我个人同意这一点,但我确实允许自己受到团队中其他人的意见的影响,如果他们争论输出参数作为参考(一些开发人员非常喜欢它们).
R. *_*ega 62
我喜欢"cplusplus.com"中一篇文章的推理.
当函数不想修改参数并且值很容易复制时传递值(int,double,char,bool等...简单类型.std :: string,std :: vector和所有其他STL容器不是简单的类型.)
当复制值很昂贵时传递const指针并且函数不想修改指向的值AND NULL是函数处理的有效预期值.
当复制值很昂贵时,通过非const指针传递并且函数想要修改指向的值AND NULL是函数处理的有效预期值.
当复制值很昂贵时,通过const引用,并且函数不想修改引用的值.如果使用指针,NULL将不是有效值.
当复制值很昂贵时,通过非连续引用,并且函数想要修改引用的值.如果使用指针,NULL将不是有效值.
在编写模板函数时,没有明确的答案,因为需要考虑的一些权衡超出了本讨论的范围,但足以说大多数模板函数通过值或(const)引用获取其参数但是因为迭代器语法类似于指针(星号到"取消引用"),任何期望迭代器作为参数的模板函数也会默认接受指针(并且不检查NULL,因为NULL迭代器概念具有不同的语法).
我从中得到的是,选择使用指针或引用参数之间的主要区别在于NULL是否是可接受的值.而已.
毕竟,值是输入,输出,可修改等应该在关于函数的文档/注释中.
澄清前面的帖子:
引用不是获取非空指针的保证.(虽然我们经常这样对待它们.)
虽然可怕的代码很糟糕,就像把你带到了破旧的坏代码后面,下面将编译并运行:(至少在我的编译器下.)
bool test( int & a)
{
return (&a) == (int *) NULL;
}
int
main()
{
int * i = (int *)NULL;
cout << ( test(*i) ) << endl;
};
Run Code Online (Sandbox Code Playgroud)
我引用的真正问题在于其他程序员,以下称为IDIOTS,他们在构造函数中分配,在析构函数中解除分配,并且无法提供复制构造函数或operator =().
突然间,foo(BAR bar)和foo(BAR & bar)之间存在着天壤之别.(调用自动按位复制操作.析构函数中的解除分配被调用两次.)
值得庆幸的是,现代编译器将获取同一指针的双重释放.15年前,他们没有.(在gcc/g ++下,使用setenv MALLOC_CHECK_ 0重新访问旧方法.)在DEC UNIX下,在同一内存中分配给两个不同的对象.那里有很多调试乐趣......
更实际的是:
就表达意图而言,这里的大多数答案都未能解决在函数签名中包含原始指针的固有歧义。问题如下:
调用者不知道指针是指向单个对象,还是指向对象“数组”的开头。
调用者不知道指针是否“拥有”它指向的内存。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;
};
Run Code Online (Sandbox Code Playgroud)
这是我能想到的最简单的界面,但它可以有效地完成工作。它允许初始化引用,检查值是否存在并访问该值。我们可以像这样使用它:
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
Run Code Online (Sandbox Code Playgroud)
我们已经实现了我们的目标!现在让我们看看与原始指针相比的好处。
nullptr可以传入,因为函数作者明确要求optional_ref我们可以从这里开始使接口更复杂,例如添加相等运算符、一元get_or和map接口、获取值或抛出异常的方法、constexpr支持。这可以由你完成。
总之,与其使用原始指针,不如推理这些指针在您的代码中的实际含义,并且要么利用标准库抽象,要么编写您自己的。这将显着改进您的代码。