C++中引用与指针的区别(详细介绍)

转载:http://www.cnblogs.com/tracylee/archive/2012/12/04/2801519.html

C++中的引用与指针的区别
指向不同类型的指针的区别在于指针类型可以知道编译器解释某个特定地址(指针指向的地址)中的内存内容及大小,而void*指针则只表示一个内存地址,编译器不能通过该指针所指向对象的类型和大小,因此想要通过void*指针操作对象必须进行类型转化。
 
    ★ 相同点:
 
    1. 都是地址的概念;
       指针指向一块内存,它的内容是所指内存的地址;
       引用是某块内存的别名。
 
    ★ 区别:
 
    1. 指针是一个实体,而引用仅是个别名;
    2. 引用使用时无需解引用(*),指针需要解引用;
    3. 引用只能在定义时被初始化一次,之后不可变;指针可变;
       引用“从一而终” ^_^
    4. 引用没有 const,指针有 const,const 的指针不可变;
    5. 引用不能为空,指针可以为空;
    6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
    typeid(T) == typeid(T&) 恒为真,sizeof(T) == sizeof(T&) 恒为真,但是当引用作为类成员名称时,其占用空间与指针相同4个字节(没找到标准的规定)。
    7. 指针和引用的自增(++)运算意义不一样;
 
    ★ 联系
 
    1. 引用在语言内部用指针实现(如何实现?)。
    2. 对一般应用而言,把引用理解为指针,不会犯严重语义错误。引用是操作受限了的指针(仅容许取内容操作)。
    引用是C++中的概念,初学者容易把引用和指针混淆一起。一下程序中,n 是m 的一个引用(reference),m 是被引用物(referent)。
    int m;
    int &n = m;
    n 相当于m 的别名(绰号),对n 的任何操作就是对m 的操作。例如有人名叫王小毛,他的绰号是“三毛”。说“三毛”怎么怎么的,其实就是对王小毛说三道四。所以n 既不是m 的拷贝,也不是指向m 的指针,其实n 就是m 它自己。
 
    引用的一些规则如下:
    (1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
    (2)不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
    (3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。
 
    以下示例程序中,k 被初始化为i 的引用。语句k = j 并不能将k 修改成为j 的引用,只是把k 的值改变成为6.由于k 是i 的引用,所以i 的值也变成了6.
 
    int i = 5;
    int j = 6;
    int &k = i;
    k = j; // k 和i 的值都变成了6;
 
 
    上面的程序看起来象在玩文字游戏,没有体现出引用的价值。引用的主要功能是传递函数的参数和返回值。C++语言中,函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。
 
    1)以下是“值传递”的示例程序。由于Func1 函数体内的x 是外部变量n 的一份拷贝,改变x 的值不会影响n, 所以n 的值仍然是0.
 
  void Func1(int x)
{
x = x + 10;
}
int n = 0;
Func1(n);
cout << “n = ” << n << endl;// n = 0

 
    2)以下是“指针传递”的示例程序。由于Func2 函数体内的x 是指向外部变量n 的指针,改变该指针的内容将导致n 的值改变,所以n 的值成为10.
void Func2(int *x)
{
(* x) = (* x) + 10;
}
&#8943;
int n = 0;
Func2(&n);
cout << “n = ” << n << endl; // n = 10

 
    3)以下是“引用传递”的示例程序。由于Func3 函数体内的x 是外部变量n 的引用,x和n 是同一个东西,改变x 等于改变n,所以n 的值成为10.
  void Func3(int &x)
{
x = x + 10;
}
&#8943;
int n = 0;
Func3(n);
cout << “n = ” << n << endl; // n = 10
 
     对比上述三个示例程序,会发现“引用传递”的性质象“指针传递”,而书写方式象“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西?
    答案是“用适当的工具做恰如其分的工作”。
    指针能够毫无约束地操作内存中的如何东西,尽管指针功能强大,但是非常危险。
    就象一把刀,它可以用来砍树、裁纸、修指甲、理发等等,谁敢这样用?
    如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖上公章的印子就行了,如果把取公章的钥匙交给他,那么他就获得了不该有的权利。
 
    ——————————
 
    摘自「高质量c++编程」
 
    指针与引用,在More Effective C++ 的条款一有详细讲述,我给你转过来
 
    条款一:指针与引用的区别
 
    指针与引用看上去完全不同(指针用操作符‘*’和‘->’,引用使用操作符‘。’),但是它们似乎有相同的功能。指针与引用都是让你间接引用其他对象。你如何决定在什么时候使用指针,在什么时候使用引用呢?
    首先,要认识到在任何情况下都不能用指向空值的引用。一个引用必须总是指向某些对象。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。
    “但是,请等一下”,你怀疑地问,“这样的代码会产生什么样的后果?”
 
    char *pc = 0;  // 设置指针为空值
    char& rc = *pc;// 让引用指向空值
 
    这是非常有害的,毫无疑问。结果将是不确定的(编译器能产生一些输出,导致任何事情都有可能发生),应该躲开写出这样代码的人除非他们同意改正错误。如果你担心这样的代码会出现在你的软件里,那么你最好完全避免使用引用,要不然就去让更优秀的程序员去做。我们以后将忽略一个引用指向空值的可能性。
 
    因为引用肯定会指向一个对象,在C++里,引用应被初始化。
    string& rs; // 错误,引用必须被初始化

    string s("xyzzy");

    string& rs = s; // 正确,rs指向s

    指针没有这样的限制。

    string *ps; // 未初始化的指针

    // 合法但危险

 
    不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。
void printDouble(const double& rd)
{
     cout << rd; // 不需要测试rd,它
} // 肯定指向一个double值
相反,指针则应该总是被测试,防止其为空:
void printDouble(const double *pd)
{
     if (pd)

     { // 检查是否为NULL
           cout << *pd;
     }
}

 
 
    指针与引用的另一个重要的不同是指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变。
  string s1("Nancy");
string s2("Clancy");
string& rs = s1; // rs 引用 s1
string *ps = &s1; // ps 指向 s1
rs = s2; // rs 仍旧引用s1,
// 但是 s1的值现在是
// "Clancy"
ps = &s2; // ps 现在指向 s2;
// s1 没有改变
 
    总的来说,在以下情况下你应该使用指针,
    一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空),
    二是你需要能够在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向)。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。
    还有一种情况,就是当你重载某个操作符时,你应该使用引用。
    最普通的例子是操作符[].这个操作符典型的用法是返回一个目标对象,其能被赋值。
  vector<int> v(10); // 建立整形向量(vector),大小为10;
// 向量是一个在标准C库中的一个模板(见条款35)
v[5] = 10; // 这个被赋值的目标对象就是操作符[]返回的值
如果操作符[]返回一个指针,那么后一个语句就得这样写:
*v[5] = 10;
 
    但是这样会使得v看上去象是一个向量指针。因此你会选择让操作符返回一个引用。(这有一个有趣的例外,参见条款30)
    当你知道你必须指向一个对象并且不想改变其指向时,或者在重载操作符并为防止不必要的语义误解时,你不应该使用指针。而在除此之外的其他情况下,则应使用指针假设你有
 
 void func(int* p, int&r);
int a = 1;
int b = 1;
func(&a,b);
   
   指针本身的值(地址值)是以pass by value进行的,你能改变地址值,但这并不会改变指针所指向的变量的值,
    p = someotherpointer; //a is still 1
 
    但能用指针来改变指针所指向的变量的值,
    *p = 123131; // a now is 123131
 
    但引用本身是以pass by reference进行的,改变其值即改变引用所对应的变量的值
    r = 1231; // b now is 1231
 
    尽可能使用引用,不得已时使用指针。
 
    当你不需要“重新指向”时,引用一般优先于指针被选用。这通常意味着引用用于类的公有接口时更有用。引用出现的典型场合是对象的表面,而指针用于对象内部。
 
    上述的例外情况是函数的参数或返回值需要一个“临界”的引用时。这时通常最好返回/获取一个指针,并使用 NULL 指针来完成这个特殊的使命。(引用应该总是对象的别名,而不是被解除引用的 NULL 指针)。
 
    注意:由于在调用者的代码处,无法提供清晰的的引用语义,所以传统的 C 程序员有时并不喜欢引用。然而,当有了一些 C++ 经验后,你会很快认识到这是信息隐藏的一种形式,它是有益的而不是有害的。就如同,程序员应该针对要解决的问题写代码,而不是机器本身。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384361.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql远程连接权限grant all privileges on *.* to ‘root‘@‘%‘ identified by ‘123456‘ with grant option语句报错

mysql远程连接权限grant all privileges on *.* to ‘root‘‘%‘ identified by ‘123456‘ with grant option语句报错记录一下自己安装mysql遇到的小坑grant all privileges on *.* to ‘root‘‘%‘ identified by ‘123456‘ with grant option只适用于mysql8.0之前的版本…

C++虚继承的概念

转载&#xff1a;http://blog.csdn.net/crystal_avast/article/details/7678704 C中虚拟继承的概念 为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题&#xff0c;将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只…

数组名和取数组名的区别

先来个简单的小案例 #include <stdio.h> #include <iostream>using namespace std;int main() {int a[10] { 0 };printf("%d\n", a);printf("%d\n", &a);printf("%d\n", a1);printf("%d\n", &a1);printf("…

C++继承详解三 ----菱形继承、虚继承

转载&#xff1a;http://blog.csdn.net/pg_dog/article/details/70175488 今天呢&#xff0c;我们来讲讲菱形继承与虚继承。这两者的讲解是分不开的&#xff0c;要想深入了解菱形继承&#xff0c;你是绕不开虚继承这一点的。它俩有着什么关系呢&#xff1f;值得我们来剖析。 菱…

Linux :IO多路复用模型

转载&#xff1a;http://blog.csdn.net/mr253727942/article/details/50827127 一、IO多路复用定义 IO多路复用允许应用在多个文件描述符上阻塞&#xff0c;并在某一个可以读写时通知&#xff0c; 一般遵循下面的设计原则&#xff1a;、 IO多路复用&#xff1a;任何文件描述符…

leetcode(一)刷题两数之和

给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 示例 1&#xff1a; 输入&#xff1a;nums [2,7,11,15], target 9 输出&#xff1a;[0,1] 解释&#xff1a;因为 nums[…

Linux并发服务器编程之多线程并发服务器

转载&#xff1a;http://blog.csdn.net/qq_29227939/article/details/53782198 上一篇文章使用fork函数实现了多进程并发服务器&#xff0c;但是也提到了一些问题&#xff1a; fork是昂贵的。fork时需要复制父进程的所有资源&#xff0c;包括内存映象、描述字等&#xff1b;目…

leetcode(二)二分法查找算法

给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9 输出: 4 解释: 9 出现在…

leetcode(977)有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,10] 输出&#xff1a;[0,1,9,16,100] 解释&#xff1a;平方后&#xff0c;数组变为 […

IO多路复用之select全面总结(必看篇)

转载&#xff1a;http://www.jb51.net/article/101057.htm 1、基本概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取&#xff0c;它就通知该进程。IO多路复用适用如下场合&#xff1a; &#xff08;1&#xff09;当客户处理多个描述字时&#xff08;一般…

leetcode(189) 旋转数组

**给定一个数组&#xff0c;将数组中的元素向右移动 k 个位置&#xff0c;其中 k 是非负数。 进阶&#xff1a; 尽可能想出更多的解决方案&#xff0c;至少有三种不同的方法可以解决这个问题。 你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗&#xff1f; 示例 1: …

I/O 多路复用之select

转载&#xff1a;http://blog.csdn.net/u012432778/article/details/47347133 概述 Linux提供了三种 I/O 多路复用方案&#xff1a;select&#xff0c;poll和epoll。在这一篇博客里先讨论select, poll 在将下一篇中介绍&#xff0c;epoll是Linux特有的高级解决方案&#xff0c;…

leetcode(283)移动零

283. 移动零 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 必须在原数组上操作&#xff0c;不能拷贝额外的数组。 尽量减少操作次数。 方法一&#xff1…

exec函数族实例解析

转载&#xff1a;http://www.cnblogs.com/blankqdb/archive/2012/08/23/2652386.html fork()函数通过系统调用创建一个与原来进程(父进程)几乎完全相同的进程(子进程是父进程的副本&#xff0c;它将获得父进程数据空间、堆、栈等资源的副本。注意&#xff0c;子进程持有的是上述…

leetcode(167)两数之和 II - 输入有序数组

两数之和 II - 输入有序数组 给定一个已按照 升序排列 的整数数组 numbers &#xff0c;请你从数组中找出两个数满足相加之和等于目标数 target 。 函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 &#xff0c;所以答案数组应当满足 …

常量指针与指针常量的区别(转帖)

转载&#xff1a;http://www.cnblogs.com/witty/archive/2012/04/06/2435311.html 三个名词虽然非常绕嘴&#xff0c;不过说的非常准确。用中国话的语义分析就可以很方便地把三个概念区分开。 一) 常量指针。 常量是形容词&#xff0c;指针是名词&#xff0c;以指针为中心的一个…

c/c++错题总结

1.类名 对象名 默认调用“对象名()”这个构造函数&#xff0c;在栈内存中存在对象名&#xff0c;在堆内存中存在实际对象&#xff1b; 2.类名 对象名(一个或以上个参数) 默认调用相应的构造函数&#xff0c;在栈内存中存在对象名&#xff0c;在堆内存中也是存在实际对象的&a…

智能指针学习笔记

转载&#xff1a;http://www.cnblogs.com/wuchanming/p/4411878.html 1. 介绍 本文介绍智能指针的使用。智能指针是c 中管理资源的一种方式&#xff0c;用智能指针管理资源&#xff0c;不必担心资源泄露&#xff0c;将c 程序员 从指针和内存管理中解脱出来&#xff0c;再者&…

c++程序编译过程

c程序编译分成四个过程&#xff1a;编译预处理&#xff0c;编译&#xff0c;汇编&#xff0c;链接 编译预处理&#xff1a;处理以#为开头 编译&#xff1a;将.cpp文件翻译成.s汇编文件 汇编&#xff1a;将.s汇编文件翻译成机器指令.o文件 链接&#xff1a;汇编生产的目标文件.o…

仿函数(函数对象)

转载&#xff1a;http://www.cnblogs.com/wuchanming/p/4411867.html 本文乃作者学习《C标准程序库》的学习笔记&#xff0c;首先介绍了仿函数&#xff08;函数对象&#xff09;和函数适配器&#xff08;配接器&#xff09;的概念&#xff0c;然后列出STL中所有的仿函数&#x…