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
c++/java/c# 几种编程语言的指针、引用比较的更多相关文章
- Java的四种引用类型之弱引用
先说结论: 首先,Java中有四种引用类型:强引用.软引用.弱引用.虚引用.-- 在 Java 1.2 中添加的,见 package java.lang.ref; . 其次,这几个概念是与垃圾回收有关 ...
- PHP、Java、Python、C、C++ 这几种编程语言都各有什么特点或优点
PHP.Java.Python.C.C++ 这几种编程语言都各有什么特点或优点 汇编: C: Java: C#: PHP: Python: Go: Haskell: Lisp: C++: &l ...
- AES加密CBC模式兼容互通四种编程语言平台【PHP、Javascript、Java、C#】
原文:AES加密CBC模式兼容互通四种编程语言平台[PHP.Javascript.Java.C#] 由于本人小菜,开始对AES加密并不了解,在网络上花了比较多时间查阅资料整理: 先简单从百度找来介绍: ...
- Java是面向对象的编程语言。它不仅吸收了C++语言的优点
Java是面向对象的编程语言.它不仅吸收了C++语言的优点,而且摒弃了C++中难于理解的多继承和指针的概念.因此,Java语言具有功能强大.使用方便的特点.Java语言作为静态面向对象的编程语言的代表 ...
- java的五种数据类型解析
不知道大家对java的简单数据类型是否了解,下面针对Java的五种类型简单数据类型表示数字和字符,进行详细的讲解和分析. 一.简单数据类型初始化 在Java语言中,简单数据类型作为类的成员变量声明时自 ...
- 用19种编程语言写Hello World
用19种编程语言写Hello World 转载自:http://www.admin10000.com/document/394.html Hello World 程序是每一种编程语言最基本的程序,通常 ...
- 浅析c++/java/c#三大热门编程语言的运行效率
从安全角度考虑,C#是这几中语言中最为安全的,它其中定义的相关安全机制很好的确保了系统的安全... 今天和同学们一起探讨下c++/java/c# 三大热门语言的运行效率情况,以及各自的用途. 估计有很 ...
- Java是一门面向对象编程语言的理解
Java是一门面向对象编程语言. 不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个特征. Java语言作为静态面向对象编程语言的 ...
- 数百种编程语言,而我为什么要学 Python?
是应用率最高.长期霸占排行榜的常青藤 Java?是易于上手,难以精通的 C?还是在游戏和工具领域仍占主流地位的 C++?亦或是占据 Windows 桌面应用程序半壁江山的 C#?…… 我想,每个人可能 ...
随机推荐
- dede 替换后台两个文件去广告
A:替换后台两个文件去广告 dede/templets路径下两个文件 1.index2.htm <!--This is IE DTD patch , Don't delete this lin ...
- 【原创】牛顿法和拟牛顿法 -- BFGS, L-BFGS, OWL-QN
数据.特征和数值优化算法是机器学习的核心,而牛顿法及其改良(拟牛顿法)是机器最常用的一类数字优化算法,今天就从牛顿法开始,介绍几个拟牛顿法算法.本博文只介绍算法的思想,具体的数学推导过程不做介绍. 1 ...
- jQuery基础知识--Form基础(续)
下拉框应用 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF ...
- 软件测试——boost单元测试 C++
分类: 1. 下载安装Boost 2. 在vs2010 中设置 工具->选项->vc++目录 设置包含文件目录:找到解压的boost文件夹eg:C:\boost_1_43_03. ...
- Redis 主从 Replication 的配置,主从复制原理
概述 Redis的replication机制允许slave从master那里通过网络传输拷贝到完整的数据备份.具有以下特点: 异步复制.从2.8版本开始,slave能不时地从master那里获取到数据 ...
- pdm 中怎么修改表的Name值时使Code值不变
修改方法:PowerDesign中的选项菜单里修改,在[Tool]-->[General Options]->[Dialog]->[Operating modes]->[Nam ...
- SQLite数据库和JPA简单介绍
SQLite数据库和JPA简单介绍 一.SQLite简单使用 SQLite是遵循ACID的关系数据库管理系统,它的处理速度很快,它的设计目标是嵌入式的,只需要几百K的内存就可以了. 1.下载SQLit ...
- Laravel不同数据库的模型之间关联
假设ModelA和ModelB之间是BelongsTo的关系 如果同属于一个数据库连接 那么 public function a(){ return $this->belongsTo(" ...
- Ansible的循环
Ansible的循环 1. 前言 有可能在一个任务中,可能要做很多事情,例如创建多个用户,安装很多个包等,那么就有可能用到循环. 2. 标准循环 重复的任务可以用下面的方式: ...
- 前端面试题(JS篇)
原题地址:http://handyxuefeng.blog.163.com/blog/static/454521722013111714040259/ 好吧,最近打算换工作,所以关注比较多的是面试题, ...