C++指针、引用和Java引用 区分

来源:互联网 发布:淘宝贷款额度突然没了 编辑:程序博客网 时间:2024/06/03 12:59

偶然联想到了这三者,便记录下来分享,也留待以后自己需要之时查阅。

先看一个简单的对比:

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

        2. 引用使用时无需解引用(*),指针需要解引用;

    3. 引用只能在定义时被初始化一次,之后不可变;指针可变;

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

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

针对C++引用的一些需要记住的规则:

  (1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。

     (2)不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)。

  (3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。


C++指针

       指针的一般形式:基类型* 指针变量名 = &变量名;

       为什么需要基类型呢?

       我们知道 int 和 char这两种类型在内存中占的字节数是不同,指针的移动距离就是以基类型为准。如果指针指向一个 int 类型变量,那么指针移动一个位置就是移动4个字节,如果指针指向一个 char 类型变量,那么指针移动一个位置就是移动1个字节。

       指针实际上还是一个变量,在内存中也有自己的空间,有自己的内存地址,不过指针存放的是另一个变量的地址。

       所以通过指针访问它指向的变量的值是需要解引用的,像“*a”这样就是解引用。

例如:

int a = 5;int *b = &a;          // &a表示取a的内存地址cout<< b <<endl;   // 输出a的地址;cout<< *b <<endl;  // 输出5cout<< &b <<endl;  // 输出b的地
              指针常用来作为函数参数传递,但实际上还是属于传值传递,不过此时传的是地址,不是一般的值。

void f(int *a){     *a = *a + 10;    //a = a +10;  不会改变实参,没有解引用}void main(){    int a = 5;    f(&a);    cout<< a <<endl;}

C++引用 

      一般形式类似:int& a = b;  //此时a就是b

      c++ 标准并没有解释编译器如何实现引用的行为。所以实现取决于编译器,而大多数情况下就是将其实现为一个 const 指针。

      C++引用实际上就是一个指针常量,表达式   int &a = b; 将会被编译器转化成 int *const a = &b;

      而引用之所以要初始化是因为 const 类型变量必须初始化,这个指针也必须有所指。这也说明了C++引用为什么不能为空,为什么不能改变,以及为什么一定要初始化。

int a = 5;int b = 6;int c = &a;c = b;  cout<< a << b << c<<endl; //输出的都是6
      上面示例中,c 先被定义为 a 的引用。然后又执行 c = b;语句,但这个语句并不会将 c 修改为 b 的引用。而只会将 b 的值赋给 c,因为 c 是 a 的引用,所以 c 改变了,a也改变。

     C++引用是会被编译器自动解引用的指针,见下面例子

int b = 1lint& a = b;cout<< &b << &a<<endl;  //输出的都是 b 的地址。
           因为引用变量会被自动解引用,所以 cout<< &a <<endl;会被解释成,cout<< &*a <<endl; 而 & 和 * 相互抵消,就相当于是 cout<< a <<endl; 而 a 值就是 b 的地址,因为其定义语句为 int* const a = &b;

    其实用 & 符号是不能获取到引用类型变量 a 的地址的,因为引用类型的变量本身只是另一个对象的别名,用可感知的方式描述,就是它仅仅是一个名字而已,对它的任何操作都是相当于对另一个对象的操作,所以这个取地址操作也是一样。所以取到的地址还是 b 的。

    C++引用变量是要占据额外空间的!占据的大小和一个指针类型一样。虽然我们都用引用是一个对象的别名来理解引用,但在汇编层次,引用其实是和指针一样的。这里给出一个实测。 戳这里:http://www.cnblogs.com/karotte/p/cpp-reference.html

 Java引用

     Java引用和C++的引用是很不相同的,要了解Java引用,先了解一下下面两个概念:  

   Java内存分配中的栈

       在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。

       当在一段代码块定义一个变量时,java就在栈中为这个变量分配内存空间,当该变量退出该作用域后,java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

   Java内存分配中的堆

       堆内存用来存放由 new 创建的对象和数组。在堆中分配的内存,由JVM的自动垃圾回收器来管理。

       在堆中产生一个对象或数组以后,还可以在栈中定义一个特殊的变量,让栈中这个变量额度取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成为了数组或对象的引用变量。引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。引用变量就相当于是为数组或者对象起的一个名称。

       引用变量是普通的变量,定义时在栈中分配,在程序运行到作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用new产生数组或对象的语句所在的代码块之外,数组和对象本身占据的内存不会释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能被使用,但仍然占据内存空间,在随后的一个不确定时间才会被垃圾回收器释放。这也是Java比较占内粗的原因。

   从上面两个概念可以看出,实际上,栈中的变量指向堆内存中的变量,这就是Java中的“指针”。

new ClassA(); //只在堆中创建了对象,并没有被引用,所以之后都无法访问。ClassA ca = new ClassA(); //在堆中创建了对象,在栈中定义了引用变量ca。之后可以通过引用变量来访问对象。
   Java引用作为方法的参数

   Java的方法参数只是传值,对象引用作为参数使用时,会给函数内引用的值的COPY,所以在方法内交换两个引用参数是没有意义的,因为方法交换的是参数的COPY值;但在方法内改变一个引用参数的属性是有意义的,因为引用参数的COPY值指向的对象和原引用指向的是同一个对象。见例子

public void swap(ClassA a,ClassA b){    ClassA tmp = a;    a = b;    b = tmp;}ClassA ca = new ClassA();ClassA cb = new ClassA();swap(ca,cb);//交换后,ca还是ca,cb还是cb
public void test(ClassA a){   a.data = 100;}ClassA a = new ClassA(50);test(a);//调用后,属性的值会被改变。
   值调用(call by value)在参数传递过程中,形参和实参占用了两个完全不同的内存空间。形参所存储的内容是实参存储内容的一份拷贝。实际上,Java对象的传递就符合这个定义,只不过形参和实参所储存的内容并不是常规意义上的变量值,而是变量的地址。咳,回过头想想:变量的地址不也是一种值吗!

    引用调用(call by reference) 在参数传递的过程中,形参和实参完全是同一块内存空间,两者不分彼此。实际上,形参名和实参名只是编程中的不同符号,在程序运行过程中,内存中存储的空间才是最重要的。不同的变量名并不能说明占用的内存存储空间不同。

    大体上说,两种调用的根本并不在于传递的是值还是地址(毕竟地址也是一个值),而是在于形参和实参是否占用同一块内存空间。

     所以Java的对象参数传递依然是值调用。

   




0 0
原创粉丝点击