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

时间: 2024-11-06 22:57:04

c++/java/c# 几种编程语言的指针、引用比较的相关文章

Java等13种编程语言名称的来历

Java的发展历程丰富多彩.充满了传奇色彩,被现在众多程序员和企业广泛使用,不用质疑这是Java的领先技术的结果. Java或者Python等编程语言,你可能每天都在使用,他们是你工作的乐趣来源和实现梦想的工具,但是你知道这些语言的名称来历吗?本文尚学堂小编为大家总结了常13种编程语言的名称来历, 快来看看你的语言名字是怎么来的吧. 可能程序员们都认为编写代码时给变量取名是件烦人的事,不过他们还需要给另外一个东西取名, 那就是新的编程语言.编程语言命名通常有几个规律,如根据特性取缩写(如BASI

Java的四种引用类型

Java中有四种引用类型:强引用.软引用.弱引用.虚引用.--应该是从1.2版本开始添加的. 这个概念是与垃圾回收有关的. 如果你不知道这几个概念,那你用的肯定都是强引用.例如String str = new String(); 这个str到 new String() 的引用类型就是强引用. 那什么是弱引用呢?先看一段代码: 1 package cn.larry.pojo; 2 3 public final class Product { 4 private String name; 5 6 p

我应该选择哪种编程语言?(Java*、C#、C++ 或 HTML5)

您是否有关于 Android* 应用的绝妙创意,而不知道如何通过可用的编程工具和语言来实现? 本文将为您介绍可用于 Android 开发的主要编程语言. 最常用的编程语言有 Java.C#.C++ 和 HTML5. 每种语言都有自己的发展历史.用途,和利弊.下面我们来一一介绍它们的工作原理.适用框架和 IDE. Java Java 作为互联网应用语言,于 1995 年首次亮相. 不过现在它应用于多个不同的开发领域,包括游戏.导航.企业解决方案和移动行业. Java 是一种经典的 Android

帮你提升 Python 的 27 种编程语言

以下为正文: 作为全球最流行的编程语言联合设计者之一,我经常看到的一种令人沮丧的行为(在Python社区和其它社区都有),就是那些有影响力的人试图把“缺失”的恐惧感灌输给其它开源社区,将其当作对社区贡献的源动力.(我偶尔会对自己的这种不当行为感到内疚,当别人落入同样的陷阱时我也就更容易察觉到). 虽然借鉴其他编程语言社区的经验是一件好事,但以恐惧为基础的方法来激励行动存在严重的问题,因为它将助涨社区成员为争取贡献者的关注而将其它社区的成员视为敌人,而不是当做潜在的盟友去迎接更大的挑战,共同推动顶

Java经典23种设计模式之结构型模式(二)

接上篇,本文介绍结构型模式里的组合模式.装饰模式.外观模式. 一.组合模式(Composite) 组合模式:将对象组合成树形结构,表示"部分--整体"的层次结构.最终达到单个对象和组合对象的使用具有一致性.单看这句话貌似有点抽象,其实比较简单. 以李云龙的独立团为例,目的要统计赵嘉宇一战共歼灭敌人多少个.最高的级别是团,一个团有若干个营,一个营有若干个排,一个排有若干个战士.(为了简化问题,排下面就不设行政单位了).很自然的,李云龙给营长开会回去给老子统计.营长回去给各个排长开会,赶紧

几种编程语言及其介绍

机器语言:纯粹的机器代码 .汇编语言:8086汇编.Win32汇编..NET的汇编 高级语言: Basic.Pascal.Object Pascal.C.C++.C#.Java.ASP.ASP.NET.Perl.PHP.SQL.FoRTRAN 等等 .Visual Basic.Visual Basic.NET.Delphi.Visual C++.C++ Builder.C# Builder.Visual Foxpro 等等. 机器语言,即二进制,非0即1,可以由计算机直接执行,效率最高,但是不适

12 种编程语言的起源故事

过去的几十年间,大量的编程语言被发明.被替换.被修改或组合在一起.每种语言总在争论中诞生,又在进化中消亡.而这些语言的的创造者,无一不是编程世界中的标志性人物,高举探索的旗帜不断前行.敬仰之情促使了这篇文章的诞生,这里收集并分享了这五十年来最成功.最流行的十二门编程语言和它们的创造者的故事. 1.Java 的起源 1990 年代初,任职于 Sun 公司的詹姆斯·高斯林等人开始开发 Java 语言的雏形,最初被命名为 Oak,目标设置在家用电器等小型系统的程序语言,应用在电视机.电话.闹钟.烤面包

【新手必看】2015年需要学习的15种编程语言!

如果你是一个程序员,那么你的美好时光来了.根据美国劳动统计局的预测,计算机方面的工作岗位将在接下来的七年时间里增长8%.如果你很擅长于写代码,那么你的薪资可以高达300美元一小时甚至更多. 想要拿到这一领域的高薪,那么你就得掌握最需要的几门编程语言. 下面是老郭--在线学习平台扣丁学堂的总监指出的2015年你需要知道的15种编程语言: 1. Java Java用于构建企业web应用的后端,是当今最为流行的编程语言之一.Web开发人员可以凭借Java和基于Java的框架为各种用户建立可扩展的Web

2015年你需要学习的15种编程语言

如果你是一个程序员,那么你的美好时光来了.根据美国劳动统计局的预测,计算机方面的工作岗位将在接下来的七年时间里增长8%.如果你很擅长于写代码,那么你的薪资可以高达300美元一小时甚至更多. 想要拿到这一领域的高薪,那么你就得掌握最需要的几门编程语言. 下面是Doug Winnie——在线学习平台Lynda的内容总监指出的2015年你需要知道的15种编程语言: 1. Java Java用于构建企业web应用的后端,是当今最为流行的编程语言之一.Web开发人员可以凭借Java和基于Java的框架为各