C++中Reference与指针(Pointer)的使用对比

reference VS pointer

1、 定义:                                              

与pointer 类似,一个reference是一个对象(object),可以用来间接指向另一个对象。

一个reference的声明与pointer的声明的实质语法结构是相同的。 例如:

int i = 3;
int *pi = &i; // pointer
int &ri = i; // reference

2、举例说明

下面使用一个针对列举类型(enumeration)的++操作符例子来说明两者的不同。在C++中, 内置的++操作符对列举类型无效,例如, 对下面定义:

enum day{
    Sunday, Monday, …
};
day x;

表达式 ++x 不能编译。如果想让它通过编译,必须要定义一个名为operator++的函数,接受day为参数,并且调用 ++x 必须改变x的值。因此, 仅声明一个函数 operator++ , 以类型day为参数, 如下:

day operator++(day d);

并不能够得到想要得效果。 这个函数通过值传递参数(pass by value),这就意味着函数内看到的是参数的一个拷贝,而不是参数本身。为了使函数能够改变其操作数(operand)的值,它必须通过指针或reference来传递其操作数。

通过指针传递参数(passing by pointer),函数定义如下:

day *operator++(day *d);

它通过将增加后的值存储到*d里面来使函数改变日期(day)的值。但是,这样你就必须使用像表达式++&x这样来调用这个操作符,这看起来不太对劲儿。

正确的方法是定义operator++以reference为参数类型,如下:

day &operator++(day &d)
{
   d = (day)(d + 1);
   return d;
}

使用这个函数, 表达式 ++x 才有正确的显示以及正确的操作。

Passing by reference不仅仅是写operator++较好的方法,而是唯一的方法。 C++在这里并没有给我们选择的余地。 像下面的声明:

day *operator++(day *d);

是不能通过编译的。每个重载的操作符函数必须或者是一个类的成员, 或者使用类型T、 T & 或 T const & 为参数类型,这里T是一个类(class)或枚举(enumeration)类型。 也就是说,每一个重载操作符必须以类或列举类型为参数类型。指针,即使是指向一个类或列举类型对象的指针,也不可以用。

C++ 不允许在重载操作符时重新定义内置操作符的含义,包括指针类型因此,我们不可以定义:

int operator++(int i); // 错误
int *operator++(int *i); // 错误

3、reference与pointer的区别

区别1:

一个pointer在它的作用域类可以指向许多不同的对象,而一个reference只能够指向一个对象。

C++ 中不允许定义”const reference”, 因为一个reference天生就是const。也就是说,一旦将一个reference绑定到一个对象,就无法再将它重新绑定到另一个不同的对象。在声 明一个reference之后没有写法可以将它重新绑定到另外一个对象。例如:

int &ri = i; 

// 将 ri 绑定到 i 。然后下面的赋值:

ri = j;
并不是把 ri 绑定到 j ,而是将 j 中的值赋给 ri 指向的对象,也就是赋给 i 。

相同点:

一个reference声明必须同时带有一个初始化赋值,如下所示:

void f()
{
   int &r = i;
    …
}

省略这个初始化赋值将产生一个编译错误:一个常量指针的声明也同样必须带有一个初始化赋值,如下所示:

void f()
{
  int *const p = &i;
  …
}

省略这个初始化赋值将产生一个编译错误.

区别2:

一个有效的reference必须指向一个对象;而一个指针不需要。一个指针,即使是一个常量指针, 都可以有空值。 一个空指针不指向任何东西。
这点不同就暗示当你想要确信一个参数必须指向一个对象的时候,应该使用reference作为参数类型。 例如,交换函数(swap function),它接受两个int参数,并将两个参数的数值对调,如下所示:

int i, j;
swap(i, j);

将原本在 i 中的值放到 j 中, 并将原本在 j 中的值放到 i 中。我们可以这样写这个函数:

void swap(int *v1, int *v2)
{
    int temp = *v1;
    *v1 = *v2;
    *v2 = temp;
}

这种定义下,函数要像这样被调用: swap(&i, &j);

这个接口暗示其中一个或两个参数都有可能为空(null)。而这个暗示是误导的。例如,调用 swap(&i, NULL); 的后果很可能是不愉快的。而像下面这样定义reference为参数:

void swap(int &v1, int &v2)
{
    int temp = v1;
    v1 = v2;
    v2 = temp;
}

清晰的表明了调用swap应该提供两个对象,它们的值将被交换。 并且这样定义的另一个好处是,在调用这个函数的时候,不需要使用那些&符号,看起来更顺眼。

4. reference更安全?

有些人认为既然reference不能够为空,那么它应该比指针更安全。 我认为reference可能要安全一点,但不会安全很多。虽然一个有效的reference不能为空,但是无效的可以呀。实际上,在很多情况下程序有可 能产生无效的reference,而不只是空的reference。 例如,你可以定义一个reference,使它绑定到一个指针指向的对象,如下所示:

    int *p;
    …
    int &r = *p;

如果指针*p在reference定义时刚好为空,则这个reference为空。 从技术上来说,这个错误并不在于将reference绑定到一个空值,而是在于对一个空指针去参考。 对一个空指针去参考产生了一个不确定的操作,也就意味着很多事都可能发生,而且大部分都不是什么好事。很有可能当程序将reference r 绑定到*p (p所指向的对象)的时候,p实际上没有被去参考,甚至程序只是将p的值拷贝给实现r的指针。而程序将会继续执行下去直到错误在后面的运行中更为明显的表 现出来,产生不可预知的危害。

下面的函数展示了另外一种产生无效reference的方法:

int &f()
{
    int i;
    …
    return i;
}

这个函数返回一个指向本地变量 i 的reference。然而当函数返回时,本地变量 i 的存储空间也就消失了。因此这个函数实际返回了一个指向被回收了的空间的reference。这个操作与返回一个指向本地变量的指针的后果相同。有些编译 器可以在编译时发现这个错误,但也很有可能不会发现。

转载于:点击打开链接


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值