为什么引用在C++中不可重用

The*_*ger 68 c++ pointers language-design reference

C++引用有两个属性:

  • 他们总是指向同一个对象.
  • 他们不能是0.

指针是相反的:

  • 他们可以指向不同的对象.
  • 他们可以是0.

为什么C++中没有"不可为空,可重复的引用或指针"?我想不出为什么参考不应该是可重复的一个很好的理由.

编辑: 问题经常出现,因为我通常使用引用,当我想确保"关联"(我在这里避免使用"引用"或"指针")永远无效.

我认为我从没想过"这个引用始终指的是同一个对象".如果引用是可重用的,那么仍然可以得到当前的行为:

int i = 3;
int& const j = i;
Run Code Online (Sandbox Code Playgroud)

这已经是合法的C++,但毫无意义.

我重申我的问题: "'引用的背后什么原因是对象'设计?为什么引用始终是同一个对象,而不是仅当声明为const时才被认为是有用的?"

干杯,菲利克斯

Mic*_*urr 88

Stroustrup的"C++的设计和演变"中给出了C++不允许重新引用引用的原因:

初始化后无法更改引用引用的内容.也就是说,一旦初始化了C++引用,就不能在以后引用其他对象; 它无法重新绑定.我过去曾被Algol68引用所咬,其中r1=r2可以分配给所引用r1的对象,也可以根据类型赋予r1(重新绑定r1)新的引用值r2.我想避免在C++中出现这样的问题.


rlb*_*ond 32

在C++中,人们常说"引用对象".从某种意义上说,确实如此:虽然在编译源代码时将引用作为指针处理,但引用旨在表示在调用函数时未复制的对象.由于引用不是可直接寻址的(例如,引用没有地址,&返回对象的地址),因此重新分配它们在语义上没有意义.而且,C++已经有了指针,它处理重新设置的语义.

  • "引用*是*对象"是错误的.除了临时的生命延长的特殊情况之外,引用的生命周期与引用的对象的生命周期不同.如果引用实际上是对象,那么当引用超出范围时,对象将被销毁(或者相反,只要引用它就会生存),而事实并非如此. (11认同)

jal*_*alf 18

因为那时你没有可重复的类型,不能为0.除非你包括3种类型的引用/指针.这会使语言变得非常复杂(然后为什么不添加第4种类型?不可重复的参考,可以是0?)

一个更好的问题可能是,为什么您希望引用可重新组合?如果是的话,这会使它们在很多情况下变得不那么有用.这会使编译器更难进行别名分析.

似乎Java或C#中引​​用的主要原因是可重用的是因为它们完成了指针的工作.他们指向对象.它们不是对象的别名.

以下是什么影响?

int i = 42;
int& j = i;
j = 43;
Run Code Online (Sandbox Code Playgroud)

在今天的C++中,使用不可重新引用的引用,它很简单.j是i的别名,我最终得到值43.

如果引用是可重新排列的,则第三行将引用j绑定到不同的值.它不再是别名,而是整数文字43(当然无效).或者可能是一个更简单(或至少在语法上有效)的例子:

int i = 42;
int k = 43;
int& j = i;
j = k;
Run Code Online (Sandbox Code Playgroud)

具有可重复的引用.在评估此代码后,j将指向k.使用C++的不可重复引用,j仍然指向i,并且我被赋值为43.

使引用可重新更改语言的语义.引用不能再是另一个变量的别名.相反,它成为一个单独的值类型,具有自己的赋值运算符.然后,最常见的参考用法之一是不可能的.没有任何东西可以获得交换.新获得的引用功能已经以指针的形式存在.所以现在我们有两种方法可以做同样的事情,而且无法用当前的C++语言做什么引用.

  • *有*已经有3种类型.可能为null,不可重定义的类型是`int*const`.为了保持一致性,应该有4个,因为可能为null且可重新组合是非常正交的. (7认同)
  • 是的,它们是正交的,但语言不必提供所有组合.问题是,增加的表现力是否会超过增加的复杂性?C++非常复杂,因此他们需要一个更好的理由来添加一个功能而不是"它还不存在". (2认同)
  • +1"for"现在我们有两种方法可以做同样的事情,而且无法用当前的C++语言做什么引用." (2认同)

dha*_*ann 7

有趣的是,这里的许多答案都有点模糊,甚至离题(例如,这并不是因为引用不能为零或相似,事实上,您可以轻松构建一个引用为零的示例)。

无法重新设置参考的真正原因相当简单。

  • 指针使您可以执行两件事:更改指针后面的值(通过->*运算符),以及更改指针本身(直接赋值=)。例子:

    int a;
    int * p = &a;
    Run Code Online (Sandbox Code Playgroud)
    1. 更改值需要取消引用:*p = 42;
    2. 改变指针:p = 0;
  • 引用允许您仅更改值。为什么?由于没有其他语法来表达重新设置。例子:

    int a = 10;
    int b = 20;
    int & r = a;
    r = b; // re-set r to b, or set a to 20?
    Run Code Online (Sandbox Code Playgroud)

换句话说,如果允许您重新设置引用,则会产生歧义。通过引用传递时更有意义:

void foo(int & r)
{
    int b = 20;
    r = b; // re-set r to a? or set a to 20?
}
void main()
{
    int a = 10;
    foo(a);
}
Run Code Online (Sandbox Code Playgroud)

希望有帮助:-)


Bri*_*ndy 5

引用不是指针,它可以在后台实现为指针,但其核心概念不等同于指针.引用应该像它*is*所指的对象一样.因此您无法更改它,也不能为NULL.

指针只是一个保存内存地址的变量. 指针本身有一个自己的内存地址,在该内存地址中它保存另一个据说指向的内存地址. 引用不相同,它没有自己的地址,因此不能更改为"保持"另一个地址.

我认为关于参考文件parashift C++ FAQ最好说:

重要说明:尽管通常使用基础汇编语言中的地址实现引用,但请不要将引用视为指向对象的有趣外观指针.参考是对象.它不是指向对象的指针,也不是对象的副本.这是对象.

FAQ 8.5中再次:

与指针不同,一旦引用绑定到对象,它就不能"重新"到另一个对象.引用本身不是一个对象(它没有标识;获取引用的地址会给出引用的地址;记住:引用是它的引用).


j_r*_*ker 5

可重新引用的引用在功能上与指针相同.

关于可空性:您无法保证在编译时这样的"可重新引用的引用"是非NULL的,因此任何此类测试都必须在运行时进行.您可以通过编写一个智能指针样式的类模板来实现这一点,该模板在初始化或分配NULL时抛出异常:

struct null_pointer_exception { ... };

template<typename T>
struct non_null_pointer {
    // No default ctor as it could only sensibly produce a NULL pointer
    non_null_pointer(T* p) : _p(p) { die_if_null(); }
    non_null_pointer(non_null_pointer const& nnp) : _p(nnp._p) {}
    non_null_pointer& operator=(T* p) { _p = p; die_if_null(); }
    non_null_pointer& operator=(non_null_pointer const& nnp) { _p = nnp._p; }

    T& operator*() { return *_p; }
    T const& operator*() const { return *_p; }
    T* operator->() { return _p; }

    // Allow implicit conversion to T* for convenience
    operator T*() const { return _p; }

    // You also need to implement operators for +, -, +=, -=, ++, --

private:
    T* _p;
    void die_if_null() const {
        if (!_p) { throw null_pointer_exception(); }
    }
};
Run Code Online (Sandbox Code Playgroud)

这可能是有用的 - 带non_null_pointer<int>参数的函数肯定会传递更多信息给调用者而不是函数int*.

  • 错误.您可以简单地保证可重新引用的引用不为null.重新启动时,您也可以使用左值,就像初始化一样.例如int foo [0] = {0}; int ref = foo [0]; ref =&= foo [1]/*重新安装ref,不分配给foo [0]*/ (4认同)
  • 此外,您可以安全地缩写为"错误".一直到0个字符. (3认同)