zoukankan      html  css  js  c++  java
  • c++/java/c# 几种编程语言的指针、引用比较

    前一段时间,我在 cnblogs 别人的博客中,谈到:

    java 中的引用/指针,与 c++/C# 中的引用/指针不是一个概念.

    Java 引用,相当于 c++ 指针(fun3)。Java 引用可以赋值 null, 而 c++ 引用 (见 fun2) 不能赋值 null,c++ 指针可以赋值 null(fun3).
    Java 中,无 c++ 引用(fun2)对应的语法。

    结果引起不必要的质疑,特此,写博客,对c++/java/c# 几种编程语言的指针、引用,进行比较,期望引起更多的人,对此有所关注。

    从语法上看,三种开发语言中,C++ 的指针、引用,最为复杂,因此,下面的举例,都从 C++ 代码开始,然后与 java/c# 的语法进行比较。

    1)  C++ 简单类型变量,有直接变量定义、指针定义、引用定义。

        int aa = 10;//c++
        int &bb = aa;//c++
        int *cc = &aa;//c++

    上述三行代码,最后三个变量指向同一个数据。相比较而言,java/c# 都只有变量定义,无引用定义、指针定义。补充:感谢 xiaotie 、飞浪 的提醒:C#中是有指针的,在unsafe状态下,可以定义和使用指针。特更正。

    2) C++ 函数调用参数,简单类型变量,有直接变量定义、指针定义、引用定义,后两个,在函数内部改变数据,退出函数,能看到改变后的数据。

    复制代码
    void simple_by_val(int a, const int b)
    {
        a=15;
        //b=13;            //error C2166: l-value specifies const object
    
        //a=NULL;        //good
        //b=NULL;        //error C2166: l-value specifies const object
    }
    
    void simple_by_ref(int &a, const int &b)
    {
        a=25;
        //b=23;            //error C2166: l-value specifies const object
    
        //a=NULL;            //good
        //b=NULL;        //error C2166: l-value specifies const object
    
    }
    
    void simple_by_pointer(int *a, const int *b)
    {
        *a = 35;
        //*b = 33;        //error C2166: l-value specifies const object
    
        a = NULL;        //ok
        b = NULL;        //ok
    }
    复制代码

    java 没有这么多名堂,只有直接变量定义。C# 略为复杂一点,有引用,有 out 参数。

            static void M(int a, ref int b, out int c)
            {
                c = 13;
            }

    相比较而言,C# 的函数参数( ref int b), 类似于C++的函数参数( int &a),都是调用函数前要赋初值,在函数内部改变数据,退出函数,能看到改变后的数据。

    而 C# 的 (out int c),在 C++/Java 中,无对应的语法。这个可以调用函数前,不赋初值。在 C# 之前,也很少见到这种语法,只在一些数据库的存储过程、函数定义中,见过类似语法。估计是从数据库编程语法中抄袭过来的语法。

    特别注明:C# 的引用( ref int b),只是用在函数参数变量定义上,不能用在函数内部的局部变量中。C++ 中的引用( int &a),可以用在函数内部的局部变量中。

    3)  C++ 的类对象变量定义语法,较为复杂,可以定义在stack 上(不用 new),可以定义在 heap(用 new)。

        CMyClass obj;                        //stack
        CMyClass *p2 = new CMyClass();        //heap

    java/C# 中,没有这么复杂,可以认为是上述两种“综合+简化”了。

    4) 在 java/C# 中,如下用法是错误的,会报空指针异常;但是在 C++ 里是合法的。

        CMyClass obj;
        obj.run();

    在 C++ 中,

    CMyClass obj;

    以上一行代码已经调用了构造函数,完成了变量初始化。而在 java/C# 中,这一行代码相当于:

    CMyClass obj = null;

     

    5) C++ 中,stack 变量出了作用范围,内存自动回收;heap 变量,需要手工 delete。

    java/C# 中,变量是空闲时自动回收的(理论上的),不是变量出了作用范围,就内存回收。

    复制代码
    {
        CMyClass obj;                        //stack
        obj.test();
    }//此处, stack 变量自动被 delete ,内存自动回收
    
    {
        CMyClass *p2 = new CMyClass();        //heap
        p2->test();
    } //此处,超出变量 p2 的作用范围,下面不能再用 p2 变量了,但是,内存并未释放,有内存泄露。
    复制代码

     

     

    6) 以下代码在 C++ 中是正确的,在 java/C# 是错误的。在 java/C# 语法中,没有定义变量加 * 的,也不能用 -> 来调用类的函数或类的成员变量,也不能用 delete。

    CMyClass *p1 = null;
    CMyClass *p2 = new CMyClass();
    p2->ab();
    delete p2;
    p2 = null;

    7) 以下代码,在java/C# 语法中,是正确的,在 C++ 是错误的。C++ 中,这种赋值要用指针 (CMyClass *p1 = null;)。

    CMyClass p1 = null;
    CMyClass p2 = new CMyClass();

    8) 以下代码,在 C++ 代码中,会调用“拷贝构造函数”、"等于号重载函数"。这两个函数,在 C++ 中,默认会由编译器自动生成。

        //C++
        CMyClass obj; //调用构造函数 CMyClass obj2 = obj; //调用拷贝构造函数 obj2 = obj; //调用 = 操作符重载函数

    以上代码,大致相当于 java/C# 中的 克隆"clone"。但更隐蔽(初学者不知道调用了 C++ 构造函数、拷贝构造函数、= 操作符重载函数)、更复杂。java/C# 无操作符重载函数。

    //C#
                CMyClass obj = new CMyClass();
                CMyClass obj2 = (CMyClass)obj.Clone();

    而在 C# 中,Clone 函数并不会自动生成。在 Java 中,可以调用 super.clone() ---- Java 基类 Object 默认有一个 clone 函数。

    在 C++ 中,默认会由编译器自动生成“拷贝构造函数”、"等于号重载函数",这一点,很多时候会造成问题,要特别注意。

    在 C++ 中,函数返回值不要用 CMyClass ,这会造成不必要地调用“拷贝构造函数”、"等于号重载函数";也不要返回引用 CMyClass&, 对函数内局部变量的引用,退出函数后无法继续使用。而要返回指针 CMyClass *(最好用智能指针包装后的指针变量)。这一点很多初学者不明白。

    但是 C++ 的 std:string 除外。std:string 的“拷贝构造函数”、"等于号重载函数"经过优化,拷贝后的变量,与拷贝之前的变量,内部使用相同的 char[] 数组,只有当一个 string 变量改变时,才会把 char[] 数组复制成两份。std:string 的“拷贝构造函数” 没有性能上损失,又比 string 指针减少了内存泄露,因此,对 std:string ,使用时尽量用 对象变量、对象引用、对象拷贝构造,避免使用 std:string 指针。

    另,java/C# 的 String 变量不可改变(有其它类,比如 java StringBuilder类是可变的),C++ 的 string 变量可以改变。这个细微差异,很多人不明白。

    9) C++ 引用语法,有一些是 Java/C# 程序员不知道的语法:

    复制代码
    //C++
    CMyClass &a1; //错误,C++ 引用变量定义的时候就要初始化;//Java/C# 对象变量,没有要求变量定义的时候,就要初始化
    
    CMyClass &a1 = NULL; //错误,C++ 引用变量不能赋值 null
    
    CMyClass &a1 = new CMyClass(); //错误,C++ 引用变量不能赋值给一个 new 对象,这种情况,要用 C++ 指针。
    
    //以下C++ 代码是正确的:
    CMyClass a;
    CMyClass &a1 = a;
    
    CMyClass *b =new CMyClass();
    CMyClass &b1 = *b; //这种写法不常用。
    复制代码

    10) Sun 自称 java 中消灭了 C++ 中万恶的指针,自己的对象变量,都是引用。做个比较:

    C++ 引用不能赋值 null, 不能赋值 new XXX();C++ 指针可以赋值 null, 可以赋值 new XXX()。

    C++ 引用对象通常在 stack 中,而C++ 指针 new 出来的对象则在 heap 中。

    java/C# 中的对象变量,可以赋值 null, 可以赋值 new XXX()。java/C# 中的对象变量在 heap 中。

     

    因此,java/C# 中的对象变量,更像是 C++ 中的指针,而不是 C++ 中的引用。

    11) C++ 中,指针变量是一个 long 型整数,可以乱指的:

    CMyClass *obj = (CMyClass *) 99;        //compile/run good, should not use    

    如果我知道一个内存地址,就可以定义一个C++指针变量,指向这个内存地址。C++ 的“引用”没有这个功能。C#/Java 的对象变量更没有这个功能。

    “指针乱指” 是 C++ 指针功能强大、灵活的体现(PC 上最早出现播放视频的时候,大概是 intel 486 CPU 时代,C++软件通常都直接写显存,据说这样速度更快),也是最容易出问题的地方。估计是因为这个原因,所以C#/Java 都去掉了这个功能。所谓“万恶的C++指针”,多半,也是指的是“指针乱指”。

    12) C++ 有野指针,即已经删除对象,但指针还是指向删除对象,还可以继续操作,但运行结果不保证正确。

    复制代码
    CMyClass *p = new CMyClass();
    ...//给 p 指向的内存赋值
    delete p;
    
    //这时 p 仍然指向之前的内存地址,该内存地址数据,一般情况下、短时间内,并没有被清空或者覆盖,仍然可以读/写。这就是“野指针”。
    p->run(); //运行结果可能正确,可能不正确,没有保证。
    
    //此时指针 p 对应的内存,可能被下一个 new XXX() 代码,用了这个内存,因此,理论上讲,delete 之后的指针,不应再用来操作对象。
    
    p= NULL; //将指针指向“空”,可以避免“野指针”问题。
    
    p->run(); //这里会报运行时错误。也就是空指针异常。空指针异常在 java/c# 中都有。
    复制代码

    C++ 中,delete 与将变量赋值 null , 理应放在一起,可以认为是一个“数据库事务”一样的,要么都成功、要么都失败。其实,delete 关键字,是由 C++ 标准定义的,标准中,完全可以要求: delete 所在行的代码,执行之后,把指针变量变成 null(C++ 标准的规范,很多都是规定编译器做什么,因此可以加这个规定)。这样可以避免野指针问题。可惜,C++ 标准,在这方面没有考虑周全。

    另,有人抱怨,面试做题,看不是是 C++ 还是 Java、C# , 期望通过看本文,可以帮助一二。

    ---------------------------------------

    欢迎大家下载试用折桂单点登录系统, http://zheguisoft.com

  • 相关阅读:
    用cdsview创建objectpage
    Promise的一个小例子
    使用CDSVIew创建List Report
    abap各种常用功能
    新建cdsview时的几个注解说明
    使用ABAP CDS views创建一个分析模型并设置参数
    使用ABAP CDS views 创建一个分析模型
    sapui5 walkthrough 26-30
    sapui5 walkthrough 21-25
    T-code 大全
  • 原文地址:https://www.cnblogs.com/zhaoxinshanwei/p/4009783.html
Copyright © 2011-2022 走看看