zoukankan      html  css  js  c++  java
  • 指针、引用与句柄(综合别人博客,方便大家和自己学习)

     

    指针、引用与句柄

    (1)指针是对象在内存中的地址;

    (2)引用是对象的别名,其实质就是功能受限但是安全性更高的指针;

    (3)句柄是指针的指针,句柄实际上是一个数据,是一个Long (整长型)的数据。句柄是一个标识符,是拿来标识对象或者项目的,它就象我们的姓名一样。Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总是如此变化,我们该到哪里去找该对象呢?为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。

    参考FAQ:

    http://www.sunistudio.com/cppfaq/references.html

    引用与指针的区别: 
    常常有人问引用与指针的区别,可能是指针和引用在功能上的相似,而是他们混淆这两个概念,
    现在总结以下二者的区别,希望大家能彻底弄清这两个概念根本性的差别:
    引用,一个变量的别名,为什么引入别名呢?原因是我们想定义一个变量,他共享另一个变量的
    内存空间,使用别名无疑是一个好的选择。变量是什么?是一个内存空间的名字,如果我们给这个
    内存空间在起另外一个名字,那就是能够共享这个内存了,引用(别名)的由此而来。
    指针,指向另一个内存空间的变量,我们可以通过它来索引另一个内存空间的内容,本身有自己的
    内存空间。
    二者区别:(1)引用访问一个变量是直接访问,而指针是间接访问。
    (2)引用是一个变量的别名,本身不单独分配自己的内存空间,而指针有自己的内存空间。
    (3)引用在开始的时候就绑定到了一个内存空间(开始必须赋初值),所以他只能是这个
    内存空间的名字,而不能改成其他的,当然可以改变这个内存空间的值.
    例如
    int i = 3,j = 4;
    int &x = i;//成为i的别名
    x = j;//不能否认x仍然引用i,并没有成为j的别名,只是修改了x和j共享的内存空间的值.
    这点与指针不同,指针在任何时刻都可以改变自己的指向
    ==========================================================================================

    你无法让引用与其指示物分离。

    和指针不同,一旦引用和对象绑定,它无法再被重新指向其他对象。引用本身不是一个对象(它没有标识;当试图获得引用的地址时,你将的到它的指示物的地址;记住:引用就是它的指示物 )。

    从某种意义上来说,引用类似 int* const p 这样的const指针(并非如 const int* p这样的指向常量的指针)。不管有多么类似,请不要混淆引用和指针;它们完全不同。

    [8.6] 何时该使用引用, 何时该使用指针?

    尽可能使用引用,不得已时使用指针。

    当你不需要“重新指向(reseating)”时,引用一般优先于指针被选用。这通常意味着引用用于类的公有接口时更有用。引用出现的典型场合是对象的表面,而指针用于对象内部。

    上述的例外情况是函数的参数或返回值需要一个“临界”的引用时。这时通常最好返回/获取一个指针,并使用 NULL指针来完成这个特殊的使命。(引用应该总是对象的别名,而不是被解除引用的 NULL 指针)。

    注意:由于在调用者的代码处,无法提供清晰的的引用语义,所以传统的 C 程序员有时并不喜欢引用。然而,当有了一些 C++经验后,你会很快认识到这是信息隐藏的一种形式,它是有益的而不是有害的。就如同,程序员应该针对要解决的问题写代码,而不是机器本身。

    引用和指针

    ★ 相同点:
    1. 都是地址的概念;
    指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。

    ★ 区别:
    1. 指针是一个实体,而引用仅是个别名;
    2. 引用使用时无需解引用(*),指针需要解引用;
    3. 引用只能在定义时被初始化一次,之后不可变;指针可变;
    引用“从一而终” ^_^
    4. 引用没有 const,指针有 const,const 的指针不可变;
    5. 引用不能为空,指针可以为空;
    6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
    typeid(T) == typeid(T&) 恒为真,sizeof(T) ==sizeof(T&) 恒为真,
    但是当引用作为成员时,其占用空间与指针相同(没找到标准的规定)。
    7. 指针和引用的自增(++)运算意义不一样;

    ★ 联系
    1. 引用在语言内部用指针实现(如何实现?)。
    2. 对一般应用而言,把引用理解为指针,不会犯严重语义错误。引用是操作受限了的指针(仅容许取内容操作)。

    C++中,指针参数和引用的区别.

    从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。

    而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。

    在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的:

    指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。

    而在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

    引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的指针,或者指针引用。

    为了进一步加深大家对指针和引用的区别,下面我从编译的角度来阐述它们之间的区别:

    程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。

    最后,总结一下指针和引用的相同点和不同点:

    ★相同点:

    ●都是地址的概念;

    指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。

    ★不同点:

    ●指针是一个实体,而引用仅是个别名;

    ●引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;

    ●引用没有const,指针有const,const的指针不可变;

    ●引用不能为空,指针可以为空;

    ●“sizeof引用”得到的是所指向的变量(对象)的大小,而“sizeof指针”得到的是指针本身的大小;

    ●指针和引用的自增(++)运算意义不一样;

    ●引用是类型安全的,而指针不是(引用比指针多了类型检查
    举个最简单的例子

    void tmp(int* b)
    {
      if (b==NULL)
      {
      b=new int;
    *b=200;
    }
    else
      {
      *b=100;
      }
    }
    void main()
    {
      int a=0;
      tmp(&a);

    int* c=NULL;
      //c=new int;
      tmp(c);
    return;
    }

    在main中有两种调用tmp函数的方法  第一个是通过引用,因为引用不可以为空,所以就是直接定义一个int.然后用引用的方法传地址过去.
    第二种方式 是指针.假如指针c在调用tmp前 ,是指向null且未分配空间,那么在tmp中会给分配空间,然后赋值.但是tmp调用完之后 c将不是tmp中赋的值.
    OK,那么我们就明白了.指针在传递时,其实就是将指针的地址copy过去的.注意,这里是copy.如果你在被调函数中修改内存指向的值,那么函数调用结束回到主调函数中,修改是有效的,也就是修改成功.这就相当于,在被调函数中,申明了一个int*的变量,其值就是传递进来的int*.
    int* b=int*c;
    那么在被调函数的堆栈中修改自己定义的int*b是 当然也是不会影响到int*c的地址.
    总来说,引用其实也是地址,C语言中的取地址符就是&
    那么,如果你想在被调函数中申请空间,那么就用引用,若是调用之前申请好空间,只是在被调函数中修改其指向的值那么引用和指针都OK.
    所以,推荐使用引用.

    C++指针使用方法解惑
    作者:hangwire

        在下列函数声明中,为什么要同时使用*和&符号?以及什么场合使用这种声明方式? 

          void func1( MYCLASS *&pBuildingElement );      
          论坛中经常有人问到这样的问题。本文试图通过一些实际的指针使用经验来解释这个问题。
      仔细看一下这种声明方式,确实有点让人迷惑。在某种意义上,"*"和"&"是意思相对的两个东西,把它们放在一起有什么意义呢?。为了理解指针的这种做法,我们先复习一下C/C++编程中无所不在的指针概念。我们都知道MYCLASS*的意思:指向某个对象的指针,此对象的类型为MYCLASS。
          void func1(MYCLASS *pMyClass);  
          
    // 例如:
          MYCLASS* p = new MYCLASS;
          func1(p);      
          上面这段代码的这种处理方法想必谁都用过,创建一个MYCLASS对象,然后将它传入func1函数。现在假设此函数要修改pMyClass:
          void func1(MYCLASS *pMyClass)
          {
             DoSomething(pMyClass);
             pMyClass = // 其它对象的指针
          }
          
          第二条语句在函数过程中只修改了pMyClass的值。并没有修改调用者的变量p的值。如果p指向某个位于地址0x008a00的对象,当func1返回时,它仍然指向这个特定的对象。(除非func1有bug将堆弄乱了,完全有这种可能。)
    现在假设你想要在func1中修改p的值。这是你的权利。调用者传入一个指针,然后函数给这个指针赋值。以往一般都是传双指针,即指针的指针,例如,CMyClass**。
          MYCLASS* p = NULL;
          func1(&p);
          
          void func1(MYCLASS** pMyClass);
          {
             *pMyClass = new MYCLASS;
             ……
          }
          
    调用func1之后,p指向新的对象。在COM编程中,你到处都会碰到这样的用法--例如在查询对象接口的QueryInterface函数中:
          interface ISomeInterface {   
             HRESULT QueryInterface(IID &iid, void** ppvObj);  
             ……  
          };  
          LPSOMEINTERFACE p=NULL;   
          pOb->QueryInterface(IID_SOMEINTERFACE, &p);  
          
        此处,p是SOMEINTERFACE类型的指针,所以&p便是指针的指针,在QueryInterface返回的时候,如果调用成功,则变量p包含一个指向新的接口的指针。
      如果你理解指针的指针,那么你肯定就理解指针引用,因为它们完全是一回事。如果你象下面这样声明函数:
          void func1(MYCLASS *&pMyClass);
          {
              pMyClass = new MYCLASS;  
              ……
          }
          
          其实,它和前面所讲得指针的指针例子是一码事,只是语法有所不同。传递的时候不用传p的地址&p,而是直接传p本身:
          
          MYCLASS* p = NULL;
          func1(p);
          
        在调用之后,p指向一个新的对象。一般来讲,引用的原理或多或少就象一个指针,从语法上看它就是一个普通变量。所以只要你碰到*&,就应该想到**。也就是说这个函数修改或可能修改调用者的指针,而调用者象普通变量一样传递这个指针,不使用地址操作符&。
      至于说什么场合要使用这种方法,我会说,极少。MFC在其集合类中用到了它--例如,CObList,它是一个CObjects指针列表。
          class CObList : public CObject {
              ……
    
              // 获取/修改指定位置的元素
              CObject*& GetAt(POSITION position);
              CObject* GetAt(POSITION position) const;
          };
          
    这里有两个GetAt函数,功能都是获取给定位置的元素。区别何在呢?
    区别在于一个让你修改列表中的对象,另一个则不行。所以如果你写成下面这样:
          CObject* pObj = mylist.GetAt(pos);
          
    则pObj是列表中某个对象的指针,如果接着改变pObj的值:
          pObj = pSomeOtherObj;
          
    这并改变不了在位置pos处的对象地址,而仅仅是改变了变量pObj。但是,如果你写成下面这样:
          CObject*& rpObj = mylist.GetAt(pos);
          
        现在,rpObj是引用一个列表中的对象的指针,所以当改变rpObj时,也会改变列表中位置pos处的对象地址--换句话说,替代了这个对象。这就是为什么CObList会有两个GetAt函数的缘故。一个可以修改指针的值,另一个则不能。注意我在此说的是指针,不是对象本身。这两个函数都可以修改对象,但只有*&版本可以替代对象。 
      在C/C++中引用是很重要的,同时也是高效的处理手段。所以要想成为C/C++高手,对引用的概念没有透彻的理解和熟练的应用是不行的。
  • 相关阅读:
    [APIO2014]序列分割
    [HNOI2014]世界树
    [THUWC2017]随机二分图
    快乐游戏鸡
    [SHOI2014]三叉神经树
    带花树学习笔记
    最小树形图——朱刘算法学习笔记
    【WC2018】即时战略
    [HNOI2015]接水果
    [HAOI2018]染色
  • 原文地址:https://www.cnblogs.com/sunliming/p/1831289.html
Copyright © 2011-2022 走看看