C、C++、Java、go的语法区别

cenglinjinran · · 2534 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

详细C++、Java比较:http://www.cnblogs.com/stephen-liu74/archive/2011/07/27/2118660.html

一、C、C++的区别 

  在很大程度上,标准C++是标准C的超集.实际上,所有C程序也是C++程序,然而,两者之间有少量区别.下面简要介绍一下最重要的区别.
1、在C++中,局部变量可以在一个程序块内在任何地方声明,在C中,局部变量必须在程序块的开始部分,即在所有"操作"语句之前声明,请注意,C99标准中取消了这种限制.
2、在C中,按如下方式声明的函数没有对函数参数进行任何说明;

int func();

   也就是说,如果没有在函数后面的括孤内指定任何参数,这在C中就意味着对函数参数未做任何声明,该函数可能有参数,也可能没有参数,然而,在C++中,这样的函数声明意味着该函数没有参数,也就是说,在C++中,下面这两个函数声明具有同样的作用:

     int func();
     int func(void);

  在C++中,参数列表中的void是任选的.许多C++程序员使用它们是为了表明函数没有任何参数的,以便于他人理解程序.但是,从技术上说,void不是必须的.
4、在C++中,所有函数均必须被设计成原型,但这在C中只是一种选择.编程经验表明,在程序中也应该给函数采用原型设计方法 .
5、在C与C++之间还存在一个重要而又细微的差别,即字符常数在C中被自动作为整形来处理,但在C++中则不然.
6、在C中,多次声明一个全局变量虽然不可取,但不算错.在C++中,多次声明同一个全局变量会引发错误.
7、在C中,一个标识符可以至少31个有效的组成字符.在C++中,一个标识符的所有组成字符均是有效的.可是,从实用角度看,过长的标识符没有太大的用处,不仅不便于记忆,而且还会增加出现打字错误的可能性.
8、在C中,在程序内部调用main()函数的情形不常见,但这种做法是容许的,在C++中,这种做法是不容许的.  
9、在C中,无法获得register型的地址,在C++中则可以获得这种地址.
10、在C中,如果类型声明语句中没有指定类型名,该类型被假定成int,这种隐式转型在C99与C++中是不允许的. 

11、头文件命名形式不同

#include <iostream>
using namespace std;

  为什么没有.h后缀名?

  名称iostream没有扩展名.h,原因是<iostream>是标准C++所定义的新式头部文件之一,新式C++头部文件不再使用.h扩展名.
  第二行告诉编译程序使用std名称空间,名称空间是C++的,一个名称空间建立一个声明范围,在该范围内可以放置各种程序元素.名称空间用来帮助组织大型程序的结构.这条using语句通知编译程序,程序员需要使用std名称空间,这是整个标准C++库声明所使用的名称空间.通过使用sdt名称空间,程序可以简化对标准库的访问.普通C程序仅使用C子集,不需要名称空间语句,因为C的库函数在默认的全局名称空间中是可使用的.

12、类与结构体的区别:类与结构体是相互关联的

  结构是C的一部分,C++从C中继承了结构,在语法上,类与结构十分相似,在关系上,这两者也很接近,在C++中,结构的作用被拓宽了,进而使结构成为了类的一种替代方法.实际上,类与结构的惟一区别在于:在默认状态下,结构的所有成员均是公有的,而类的所有成员是私有的.除此之外,类与结构是等价的,也就是说,一个结构定义了一个类的类型.
  C++同时包含这两个等价的关键字struct与class,基于3个方面的原因:

  第一,加强结构的能力.在C中,结构提供了一种数据分组方法,因而让结构包含成员函数是一个小小的改进;

  第二,由于类与结构是相互关联的,所有现有C代码到C++的移植变得更容易;

  第三,由于类与结构的等价性,提供两个不同的关键字可以使类定义自由发展,为了保持C++与C的兼容性,结构定义必须始终受它的C定义的结束。
  即使在有些地方可以使用结构来代替类,但尽量不要这么做,为了清楚起见,该用类的地方就用class关键字,该用C结构的地方就用struct关键字.

13、类与联合是相互关联的

  联合也可以用来定义类.在C++中,联合包含成员函数,变量以及构造与析构函数.C++联合保留了C联合的全部特征,其中最重要的特征是所有数据元素共享内存的相同地址.与结构类似,联合的成员在默认状态下也是公有的,并且完全兼容于C.与结构一样,C++中的联合声明定义了一种特殊的类,进而意味着保持了类的封装原则.
  C++的联合有几个必须遵守的使用限制.第一,联合不能继承其他任何类型的类.第二,联合不能是基类,不能包含有虚函数成员.静态变量不能是联合的成员.联合不能使用引用成员,而且不能有任何作为成员的重载赋值运算符的对象.第三,如果一个对象包含明确的构造或析构函数,该对象不能成为联合的成员.
  C++有一个叫做匿名联合的特殊联合.匿名联合没有类型名,也不声明任何变量,只是告诉编译程序它的成员变量共享一个内存地址.但是,变量本身无需要使用常规的点运算符语法即可直接引用。
  上述联合的使用限制也适用于匿名联合,但下面这两个限制除外,第一,匿名联合所包含的元素只能是数据,不能包含成员函数,也不能包含私有或受保护元素;第二,全局匿名联合必须声明成静态的.

 

二、java与C++的区别

1、和C++一样,Java也提供了两种类型的注释。

2、所有东西都必须置入一个类。不存在全局函数或者全局数据。如果想获得与全局函数等价的功能,可考虑将static方法和static数据置入一个类里。注意没有象结构、枚举或者联合这一类的东西,一切只有“类”(Class)!
3、所有方法都是在类的主体定义的。所以用C++的眼光看,似乎所有函数都已嵌入,但实情并非如何(嵌入的问题在后面讲述)。
4、在Java中,类定义采取几乎和C++一样的形式。但没有标志结束的分号。没有class foo这种形式的类声明,只有类定义。

  class aType(){
  void aMethod() {/* 方法主体*/}
  }

5、Java中没有作用域范围运算符“::”。Java利用点号做所有的事情,但可以不用考虑它,因为只能在一个类里定义元素。即使那些方法定义,也必须在一个类的内部,所以根本没有必要指定作用域的范围。我们注意到的一项差异是对static方法的调用:使用ClassName.methodName()。除此以外,package(包)的名字是用点号建立的,并能用import关键字实C++的“#include”的一部分功能。例如下面这个语句:

import java.awt.*;

 (#include并不直接映射成import,但在使用时有类似的感觉。)
6、与C++类似,Java含有一系列“主类型”(Primitive type),以实现更有效率的访问。在Java中,这些类型包括boolean,char,byte,short,int,long,float以及double。所有主类型的大小都是固有的,且与具体的机器无关(考虑到移植的问题)。这肯定会对性能造成一定的影响,具体取决于不同的机器。对类型的检查和要求在Java里变得更苛刻。例如:
  ■条件表达式只能是boolean(布尔)类型,不可使用整数。
  ■必须使用象X+Y这样的一个表达式的结果;不能仅仅用“X+Y”来实现“副作用”。
7、char(字符)类型使用国际通用的16位Unicode字符集,所以能自动表达大多数国家的字符。
8、静态引用的字串会自动转换成String对象。和C及C++不同,没有独立的静态字符数组字串可供使用。
9、Java增添了三个右移位运算符“>>>”,具有与“逻辑”右移位运算符类似的功用,可在最末尾插入零值。“>>”则会在移位的同时插入符号位(即“算术”移位)。
10、尽管表面上类似,但与C++相比,Java数组采用的是一个颇为不同的结构,并具有独特的行为。有一个只读的length成员,通过它可知道数组有多大。而且一旦超过数组边界,运行期检查会自动丢弃一个异常。所有数组都是在内存“堆”里创建的,我们可将一个数组分配给另一个(只是简单地复制数组句柄)。数组标识符属于第一级对象,它的所有方法通常都适用于其他所有对象。
11、对于所有不属于主类型的对象,都只能通过new命令创建。和C++不同,Java没有相应的命令可以“在堆栈上”创建不属于主类型的对象。所有主类型都只能在堆栈上创建,同时不使用new命令。所有主要的类都有自己的“封装(器)”类,所以能够通过new创建等价的、以内存“堆”为基础的对象(主类型数组是一个例外:它们可象C++那样通过集合初始化进行分配,或者使用new)。
12、Java中不必进行提前声明。若想在定义前使用一个类或方法,只需直接使用它即可——编译器会保证使用恰当的定义。所以和在C++中不同,我们不会碰到任何涉及提前引用的问题。
13、Java没有预处理机。若想使用另一个库里的类,只需使用import命令,并指定库名即可。不存在类似于预处理机的宏。
14、Java用包代替了命名空间。由于将所有东西都置入一个类,而且由于采用了一种名为“封装”的机制,它能针对类名进行类似于命名空间分解的操作,所以命名的问题不再进入我们的考虑之列。数据包也会在单独一个库名下收集库的组件。我们只需简单地“import”(导入)一个包,剩下的工作会由编译器自动完成。
15、被定义成类成员的对象句柄会自动初始化成null。对基本类数据成员的初始化在Java里得到了可靠的保障。若不明确地进行初始化,它们就会得到一个默认值(零或等价的值)。可对它们进行明确的初始化(显式初始化):要么在类内定义它们,要么在构建器中定义。采用的语法比C++的语法更容易理解,而且对于static和非static成员来说都是固定不变的。我们不必从外部定义static成员的存储方式,这和C++是不同的。
16、在Java里,没有象C和C++那样的指针。用new创建一个对象的时候,会获得一个引用。例如:

String s = new String("howdy");

  然而,C++引用在创建时必须进行初始化,而且不可重定义到一个不同的位置。但Java引用并不一定局限于创建时的位置。它们可根据情况任意定义,这便消除了对指针的部分需求。在C和C++里大量采用指针的另一个原因是为了能指向任意一个内存位置(这同时会使它们变得不安全,也是Java不提供这一支持的原因)。指针通常被看作在基本变量数组中四处移动的一种有效手段。Java允许我们以更安全的形式达到相同的目标。解决指针问题的终极方法是“固有方法”(已在附录A讨论)。将指针传递给方法时,通常不会带来太大的问题,因为此时没有全局函数,只有类。而且我们可传递对对象的引用。Java语言最开始声称自己“完全不采用指针!”但随着许多程序员都质问没有指针如何工作?于是后来又声明“采用受到限制的指针”。大家可自行判断它是否“真”的是一个指针。但不管在何种情况下,都不存在指针“算术”。
17、Java提供了与C++类似的“构建器”(Constructor)。如果不自己定义一个,就会获得一个默认构建器。而如果定义了一个非默认的构建器,就不会为我们自动定义默认构建器。这和C++是一样的。注意没有复制构建器,因为所有自变量都是按引用传递的。
18、Java中没有“破坏器”(Destructor)。变量不存在“作用域”的问题。一个对象的“存在时间”是由对象的存在时间决定的,并非由垃圾收集器决定。有个finalize()方法是每一个类的成员,它在某种程度上类似于C++的“破坏器”。但finalize()是由垃圾收集器调用的,而且只负责释放“资源”(如打开的文件、套接字、端口、URL等等)。如需在一个特定的地点做某样事情,必须创建一个特殊的方法,并调用它,不能依赖finalize()。而在另一方面,C++中的所有对象都会(或者说“应该”)破坏,但并非Java中的所有对象都会被当作“垃圾”收集掉。由于Java不支持破坏器的概念,所以在必要的时候,必须谨慎地创建一个清除方法。而且针对类内的基础类以及成员对象,需要明确调用所有清除方法。
19、Java具有方法“过载”机制,它的工作原理与C++函数的过载几乎是完全相同的。
20、Java不支持默认自变量。
21、Java中没有goto。它采取的无条件跳转机制是“break 标签”或者“continue 标准”,用于跳出当前的多重嵌套循环。
22、Java采用了一种单根式的分级结构,因此所有对象都是从根类Object统一继承的。而在C++中,我们可在任何地方启动一个新的继承树,所以最后往往看到包含了大量树的“一片森林”。在Java中,我们无论如何都只有一个分级结构。尽管这表面上看似乎造成了限制,但由于我们知道每个对象肯定至少有一个Object接口,所以往往能获得更强大的能力。C++目前似乎是唯一没有强制单根结构的唯一一种OO语言。
23、Java没有模板或者参数化类型的其他形式。它提供了一系列集合:Vector(向量),Stack(堆栈)以及Hashtable(散列表),用于容纳Object引用。利用这些集合,我们的一系列要求可得到满足。但这些集合并非是为实现象C++“标准模板库”(STL)那样的快速调用而设计的。Java 1.2中的新集合显得更加完整,但仍不具备正宗模板那样的高效率使用手段。
24、“垃圾收集”意味着在Java中出现内存漏洞的情况会少得多,但也并非完全不可能(若调用一个用于分配存储空间的固有方法,垃圾收集器就不能对其进行跟踪监视)。然而,内存漏洞和资源漏洞多是由于编写不当的finalize()造成的,或是由于在已分配的一个块尾释放一种资源造成的(“破坏器”在此时显得特别方便)。垃圾收集器是在C++基础上的一种极大进步,使许多编程问题消弥于无形之中。但对少数几个垃圾收集器力有不逮的问题,它却是不大适合的。但垃圾收集器的大量优点也使这一处缺点显得微不足道。
25、Java内建了对多线程的支持。利用一个特殊的Thread类,我们可通过继承创建一个新线程(放弃了run()方法)。若将synchronized(同步)关键字作为方法的一个类型限制符使用,相互排斥现象会在对象这一级发生。在任何给定的时间,只有一个线程能使用一个对象的synchronized方法。在另一方面,一个synchronized方法进入以后,它首先会“锁定”对象,防止其他任何synchronized方法再使用那个对象。只有退出了这个方法,才会将对象“解锁”。在线程之间,我们仍然要负责实现更复杂的同步机制,方法是创建自己的“监视器”类。递归的synchronized方法可以正常运作。若线程的优先等级相同,则时间的“分片”不能得到保证。
26、我们不是象C++那样控制声明代码块,而是将访问限定符(public,private和protected)置入每个类成员的定义里。若未规定一个“显式”(明确的)限定符,就会默认为“友好的”(friendly)。这意味着同一个包里的其他元素也可以访问它(相当于它们都成为C++的“friends”——朋友),但不可由包外的任何元素访问。类——以及类内的每个方法——都有一个访问限定符,决定它是否能在文件的外部“可见”。private关键字通常很少在Java中使用,因为与排斥同一个包内其他类的访问相比,“友好的”访问通常更加有用。然而,在多线程的环境中,对private的恰当运用是非常重要的。Java的protected关键字意味着“可由继承者访问,亦可由包内其他元素访问”。注意Java没有与C++的protected关键字等价的元素,后者意味着“只能由继承者访问”(以前可用“private protected”实现这个目的,但这一对关键字的组合已被取消了)。
27、嵌套的类。在C++中,对类进行嵌套有助于隐藏名称,并便于代码的组织(但C++的“命名空间”已使名称的隐藏显得多余)。Java的“封装”或“打包”概念等价于C++的命名空间,所以不再是一个问题。Java 1.1引入了“内部类”的概念,它秘密保持指向外部类的一个句柄——创建内部类对象的时候需要用到。这意味着内部类对象也许能访问外部类对象的成员,毋需任何条件——就好象那些成员直接隶属于内部类对象一样。这样便为回调问题提供了一个更优秀的方案——C++是用指向成员的指针解决的。
28、由于存在前面介绍的那种内部类,所以Java里没有指向成员的指针。
29、Java不存在“嵌入”(inline)方法。Java编译器也许会自行决定嵌入一个方法,但我们对此没有更多的控制权力。在Java中,可为一个方法使用final关键字,从而“建议”进行嵌入操作。然而,嵌入函数对于C++的编译器来说也只是一种建议。
30、Java中的继承具有与C++相同的效果,但采用的语法不同。Java用extends关键字标志从一个基础类的继承,并用super关键字指出准备在基础类中调用的方法,它与我们当前所在的方法具有相同的名字(然而,Java中的super关键字只允许我们访问父类的方法——亦即分级结构的上一级)。通过在C++中设定基础类的作用域,我们可访问位于分级结构较深处的方法。亦可用super关键字调用基础类构建器。正如早先指出的那样,所有类最终都会从Object里自动继承。和C++不同,不存在明确的构建器初始化列表。但编译器会强迫我们在构建器主体的开头进行全部的基础类初始化,而且不允许我们在主体的后面部分进行这一工作。通过组合运用自动初始化以及来自未初始化对象句柄的异常,成员的初始化可得到有效的保证。

  public class Foo extends Bar {
   public Foo(String msg) {
   super(msg); // Calls base constructor
   }
   public baz(int i) { // Override
   super.baz(i); // Calls base method
   }
  }

31、Java中的继承不会改变基础类成员的保护级别。我们不能在Java中指定public,private或者protected继承,这一点与C++是相同的。此外,在衍生类中的优先方法不能减少对基础类方法的访问。例如,假设一个成员在基础类中属于public,而我们用另一个方法代替了它,那么用于替换的方法也必须属于public(编译器会自动检查)。
32、Java提供了一个interface关键字,它的作用是创建抽象基础类的一个等价物。在其中填充抽象方法,且没有数据成员。这样一来,对于仅仅设计成一个接口的东西,以及对于用extends关键字在现有功能基础上的扩展,两者之间便产生了一个明显的差异。不值得用abstract关键字产生一种类似的效果,因为我们不能创建属于那个类的一个对象。一个abstract(抽象)类可包含抽象方法(尽管并不要求在它里面包含什么东西),但它也能包含用于具体实现的代码。因此,它被限制成一个单一的继承。通过与接口联合使用,这一方案避免了对类似于C++虚拟基础类那样的一些机制的需要。
  为创建可进行“例示”(即创建一个实例)的一个interface(接口)的版本,需使用implements关键字。它的语法类似于继承的语法,如下所示:

public interface Face {
   public void smile();
}
public class Baz extends Bar implements Face {
   public void smile( ) {
     System.out.println("a warm smile");
   }
}

33、Java中没有virtual关键字,因为所有非static方法都肯定会用到动态绑定。在Java中,程序员不必自行决定是否使用

34、指针
JAVA语言让编程者无法找到指针来直接访问内存无指针,并且增添了自动的内存管理功能,从而有效地防止了c/c++语言中指针操作失误,如野指针所造成的系统崩溃。但也不是说JAVA没有指针,虚拟机内部还是使用了指针,只是外人不得使用而已。这有利于Java程序的安全。 
35、多重继承 
C++支持多重继承,这是C++的一个特征,它允许多父类派生一个类。尽管多重继承功能很强,但使用复杂,而且会引起许多麻烦,编译程序实现它也很不容易。Java不支持多重继承,但允许一个类继承多个接口(extends+implement),实现了C++多重继承的功能,又避免了C++中的多重继承实现方式带来的诸多不便。 
36、数据类型及类 
Java是完全面向对象的语言,所有函数和变量部必须是类的一部分。除了基本数据类型之外,其余的都作为类对象,包括数组。对象将数据和方法结合起来,把它们封装在类中,这样每个对象都可实现自己的特点和行为。而c++允许将函数和变量定义为全局的。此外,Java中取消了c/c++中的结构和联合,消除了不必要的麻烦。 
37、自动内存管理 
Java程序中所有的对象都是用new操作符建立在内存堆栈上,这个操作符类似于C++的new操作符。下面的语句由一个建立了一个类Read的对象,然后调用该对象的work方法:

Read r=new Read();

r.work();

语句

Read r=new Read();

在堆栈结构上建立了一个Read的实例。Java自动进行无用内存回收操作,不需要程序员进行删除。而c++中必须由程序员释放内存资源,增加了程序设计者的负担。Java中当一个对象不被再用到时,无用内存回收器将给它加上标签以示删除。Java里无用内存回收程序是以线程方式在后台运行的,利用空闲时间工作。 
38、操作符重载 
Java不支持操作符重载。操作符重载被认为是C++的突出特征,在Java中虽然类大体上可以实现这样的功能,但操作符重载的方便性仍然丢失了不少。Java语言不支持操作符重载是为了保持Java语言尽可能简单。 
39、预处理功能 
Java不支持预处理功能。C/C++在编译过程中都有一个预编译阶段,即众所周知的预处理器。预处理器为开发人员提供了方便,但增加丁编译的复杂性。JAVA虚拟机没有预处理器,但它提供的引入语句(import)与C++预处理器的功能类似。 
40、 Java不支持缺省函数参数,C++支持 
在C中,代码组织在函数中,函数可以访问程序的全局变量。C++增加了类,提供了类算法,该算法是与类相连的函数,C++类方法与Java类方法十分相似,然而,由C++仍然支持C,所以不能阻止C++开发人员使用函数,结果函数和方法混合使用使得程序比较混乱。 Java没有函数,作为一个比c++更纯的面向对象的语言,Java强迫开发人员把所有例行程序包括在类中,事实上,用方法实现例行程序可激励开发人员更好地组织编码。 
41、字符串 
C和C++不支持字符串变量,在C和C++程序中使用null终止符代表字符串的结束,在Java中字符串是用类对象(String和StringBuffer)来实现的,这些类对象是Java语言的核心,用类对象实现字符串有以下几个优点: 
■在整个系统中建立字符串和访问字符串元素的方法是一致的; 
■字符串类是作为Java语言的一部分定义的,而不是作为外加的延伸部分; 
■Java字符串执行运行时检空,可帮助排除一些运行时发生的错误; 
■可对字符串用“十”进行连接操作。
42、数组
java引入了真正的数组。不同于C++中利用指针实现的“伪数组”,Java引入了真正的数组,同时将容易造成麻烦的指针从语言中去掉,这将有利于防止在C++程序中常见的因为数组操作越界等指针操作而对系统数据进行非法读写带来的不安全问题。
43、“goto“语句 
“可怕”的goto语句是C和C++的“遗物”,它是该语言技术上的合法部分,引用goto语句引起了程序结构的混乱,不易理解,goto语句子要用于无条件转移子程序和多结构分支技术。鉴于以广理由,Java不提供goto语句,它虽然指定goto作为关键字,但不支持它的使用,使程序简洁易读。 
44、类型转换 
在C和C++中有时出现数据类型的隐含转换,这就涉及了自动强制类转换问题。例如,在c++中可将一浮点值赋予整型变量,并去掉其尾数。Java不支持C++中的自动强制类型转换,如果需要,必须由程序显式进行强制类型转换。 
45、异常 
JAVA中的异常机制用于捕获例外事件,增强系统容错能力

try{

//可能产生例外的代码

}catch(exceptionType name){

//处理

}

其中exceptionType表示异常类型。而C++则没有如此方便的机...

 

 

三、Go和C++的区别 

1、变量初始化

现在开发语言的初始化都差不多,都能很方便的定义时初始化,循环也是如下面中的C++ for循环和Go语言中的"for : rang"形式基本一样。 另外C++的auto,Go语言中的":=",都能省代码的好东西。不过要多提一句,Go语言支持多重赋值,并且变量都是默认就已初始化好。同时,Go语言也支持指针,但它的指针要安全得多。

Golang 

var k int
fmt.Println(k)
 
mArr := []int{ 1, 2, 3}
var mMap = map[string]int {"a":1,"b":2}
fmt.Printf("array:%v\nmap:%v \n",mArr,mMap)
 
i := 10  
pi := &i  //*i
ppi := π  //**int
fmt.Println(i,*pi,**ppi)
结果:
0
array:[1 2 3]
map:map[a:1 b:2]
10 10 10

 C++

int mArr[] = { 1, 2, 3 };
  auto mList = vector < int > {1, 2, 3, 4};
  auto mMap = map < int, string > {{1, "aa"}, { 2, "bb" }};
 
  cout << "vector: ";
  for (const int& x : mList)
    cout << x << " ";
  cout << endl;
 
  cout << "map: ";
  for (const auto& mp : mMap)
    cout << mp.first << " " << (mp.second).c_str();
  cout << endl;

 

2、lambda表达式

lambda这东西在C++11中可是重点推荐的特性,非常的强大。Go语言自然也有,但对于闭包函数中函数外部变量的处理并没有C++那么多种。 像C++分了四类:

[a,&b] a变量以值的方式呗捕获,b以引用的方式被捕获。 
[this] 以值的方式捕获 this 指针。 
[&] 以引用的方式捕获所有的外部自动变量。 
[=] 以值的方式捕获所有的外部自动变量。 
[] 不捕获外部的任何变量。

而Go语言默认就相当于"[=]",即,捕获可见范围内所有的外部变量。

Golang

mArr := []int{ 1, 2, 3}
  fun := func(i,v int){
    fmt.Printf("idx:%d value:%d \n",i,v)
  }
  for idx,val := range mArr{
    fun(idx,val)
  }

 C++    

int arr[] = { 1, 2, 3 };
  for_each(begin(arr), end(arr), [](int n){cout << n << endl; });
 
  cout << "---lambda_demo---" << endl;
 
  auto func = [](int n){cout << n << endl; };
  for_each(begin(arr), end(arr), func);

3、值顺序递增(iota)

iota这个小东西很有特点,两种语言都支持且都是让数据顺序递增,从功能上看C++的iota似乎更强大灵活些。 但有意思的是,似乎在Go语言中,iota的使用频率要高得多,被大量的用于像const定义之类的地方,有意思。

Golang

const (
    Red = iota
    Green
    Blue
  )
  fmt.Println("Red:",Red," Gree:",Green," Blue:",Blue);

C++

int d[5] = { 0 };
std::iota(d, d + 5, 10);
 
cout << "iota_demo(): old : d[5] = { 0 } "<< endl;
cout << "iota_demo(): iota: d[5] = { ";
for each (int var in d)
{
  cout <<  var <<" ";
}
cout <<"} "<< endl;
 
char e[5] = { 'a' };
char f[5] = { 0 };
copy_n(e, 5, f);
 
cout << "iota_demo(): old : e[5] = { 'a' } " << endl;
cout << "iota_demo(): iota: e[5]  " << endl;
std::iota(e, e + 5, 'e');
for (size_t i = 0; i < 5; i++)
{
  cout << "                      old = " << f[i] << " iota = " << e[i] << endl;
}
结果:
iota_demo(): old : d[5] = { 0 }
iota_demo(): iota: d[5] = { 10 11 12 13 14 }
iota_demo(): old : e[5] = { 'a' }
iota_demo(): iota: e[5]
 iota = e
 iota = f
 iota = g
 iota = h
 iota = i

4、多值赋值及函数返回多值

这个功能在Go语言中相当方便,C++中则需要使用tuple和 tie等才能实现,有点麻烦,但效果是一样的。

Golang

func tuple_demo()(int,string){
 
  a,b := 1,2
  fmt.Println("a:",a," b:",b);
 
  c,d := b,a
  fmt.Println("c:",c," d:",d);
 
  return 168, "函数返回的字符串"
}

 C++

tuple<int, string> tuple_demo(){
  tuple<int, string> ret;
  ret = make_tuple(168, "函数返回的字符串");
  cout << "tuple_demo(): " << get<0>(ret) << " " << (get<1>(ret)).c_str() << endl;
 
  auto triple = make_tuple(5, 6, 7);
  cout << "tuple_demo(): " << get<0>(triple) << " " << get<1>(triple) << " " << get<2>(triple) << endl;
 
  int ti;
  string ts;
  tie(ti, ts) = make_tuple(10, "xcl--将数字和字符赋值给两个变量");
  cout << "tuple_demo(): " << ti << " " << ts.c_str() << endl;
 
  return ret;
}
 
//调用:
int ti;
string ts;
tie(ti, ts) = tuple_demo();
cout << "main() <- tuple_demo(): " << ti << " " << ts.c_str() << endl;
结果:
tuple_demo(): 168 函数返回的字符串
tuple_demo(): 5 6 7
tuple_demo(): 10 xcl--将数字和字符赋值给两个变量
main() <- tuple_demo(): 168 函数返回的字符串

5、map查找

Go语言中map的查找特别方便. 要找个值,直接map[key]就出来了。C++也可以直接用find(key)的方式,但Go语言直接有个found的匿名变量,能告知是否有找到,这个要比C++去比end(),要直观些,也可以少打些字。

Golang

var mMap = map[string]int {"a":1,"b":2,"c":3}
  val,found := mMap["b"]
  if found {
    fmt.Println("found :",val);
  }else{
    fmt.Println("not found");
  }

C++    

typedef map <string, int > map_str_int;
tuple<string, int, bool> mapfind_demo(map_str_int myMap, string key){
  
  map_str_int::iterator pos;   
  pos = myMap.find(key);
  if (pos == myMap.end()){
    return make_tuple("", 0, false);
  }
  else{
    return make_tuple(pos->first, pos->second, true);
  }
}
调用:
auto myMap = map_str_int{ { "aa", 1 }, { "bb", 2 }, { "cc", 3 } };
 
string mpKey;
int mpValue;
bool  mpFound = false;
tie(mpKey, mpValue, mpFound) = mapfind_demo(myMap, "bb");
 
if (mpFound){
  cout << "mapfind_demo: found" << endl;
}
else{
  cout << "mapfind_demo: not found" << endl;
}

6、可变参数

可变参数是指函数的最后一个参数可以接受任意个参数,我在用Go语言实现的args_demo()例子中,用了效果一样的两种不同

调用方法来展示Go语言对这个下的功夫。然后可以再看看通过C++模板实现的,一个比较有代表性的Print函数来感受感受C++

对这个可变参数的处理方式。

Golang  

auto myMap = map_str_int{ { "aa", 1 }, { "bb", 2 }, { "cc", 3 } };
 
string mpKey;
int mpValue;
bool  mpFound = false;
tie(mpKey, mpValue, mpFound) = mapfind_demo(myMap, "bb");
 
if (mpFound){
  cout << "mapfind_demo: found" << endl;
}
else{
  cout << "mapfind_demo: not found" << endl;
}
调用
args_demo(5,6,7,8);
mArr := []int{ 5, 6, 7,8}
args_demo(mArr[0],mArr[1:]...);
fmt.Println("fmtPrintln(): ", 1, 2.0, "C++11", "Golang");
执行结果
ars_demo first: 5
args: 6
args: 7
args: 8
ars_demo first: 5
args: 6
args: 7
args: 8
fmtPrintln():  1 2 C++11 Golang

C++

template<typename T> void fmtPrintln(T value){
  cout << value << endl;
}
 
template<typename T, typename... Args>
void fmtPrintln(T head, Args... args)
{
  cout << head << " ";
  fmtPrintln(args...);
}
调用
fmtPrintln("fmtPrintln(): ", 1, 2.0, "C++11", "Golang");
执行结果:
fmtPrintln():  1 2 C++11 Golang

7、回调函数

对比看看两种语言函数的回调处理。实现方法没啥差异。

Golang

type funcType func(string)
func printFunc(str string){
  fmt.Println( "callFunc() -> printFunc():",str)
}
func callFunc(arg string,f funcType){
  f(arg)
}
callFunc("回调就是你调我,我调它,大家一起玩。",printFunc)

C++

void printFunc(string arg){
  cout << "callFunc() -> printFunc():" << arg.c_str() << endl;
}
typedef void(*callf)(string);
void callFunc(callf pFunc, string arg){ //void(*pFunc)(string)
  pFunc(arg);
}
callFunc(printFunc, "回调就是你调我,我调它,大家一起玩。");

8、泛型

C++泛型就不用多说了,都知道有多强大。Go语言要加入这个不知道是啥时候的事,不过通过简单的反射,还是可以实现类似的功能,但代码有点长。

Golang

func compare(v1, v2 interface{}) (int, error) {
 
  switch v1.(type) {
  case int:
    if v1.(int) < v2.(int) {
      return -1, nil
    } else if v1.(int) == v2.(int) {
      return 0, nil
    }
  case int8:
    if v1.(int8) < v2.(int8) {
      return -1, nil
    } else if v1.(int8) == v2.(int8) {
      return 0, nil
    }
  case int32:
    if v1.(int32) < v2.(int32) {
      return -1, nil
    } else if v1.(int32) == v2.(int32) {
      return 0, nil
    }
 
  //省略......
 
  default:
    return -2, errors.New("未能处理的数据类型.")
  }
  return 1, nil
}

 调用:

v1 := 13
v2 := 53
ret, err := compare(v1, v2)
if err != nil {
  fmt.Println(err)
  return
}
switch ret {
case -1:
  fmt.Println("v1 < v2")
case 0:
  fmt.Println("v1 == v2")
case 1:
  fmt.Println("v1 > v2")
default:
  fmt.Println("defualt")
}

C++

template <typename T> int compare(const T v1, const T v2){
  if (v1 < v2){
    cout << "compare(): v1 < v2" << endl;
    return -1;
  }
  else if (v1 == v2){
    cout << "compare(): v1 == v2" << endl;
    return 0;
  }
  else{
    cout << "compare(): v1 > v2" << endl;
    return 1;
  }
}
调用:
int i1 = 5, i2 = 7;
double d1 = 52.5, d2 = 10.7;
compare(i1, i2);
compare(d1, d2);

9、数组和切片(sclie)

数组/切片Go语言做得非常灵活,不一一举例,这里主要可以看看C++的。我用copy_n,copy_if 模拟二下简单的切片功能。

Golang 

a := [5]int{ 1, 2, 3, 4, 5 }
    b := a[:3]
    c := a[1:2]
    fmt.Println(a)
    fmt.Println(b)
    fmt.Println(c)   
运行结果:
[1 2 3 4 5]
[1 2 3]
[2]

C++    

int a[5] = { 1, 2, 3, 4, 5 };
  int b[3] = { 0 };
  int c[2] = { 0 };
 
 
  cout << "a[5] = { 1, 2, 3, 4, 5 }" << endl;
  cout << "array[:end_pos]: b = array[:3]" << endl;
  // array[:end_pos]
  copy_n(a, 3, b);
  for each (int var in b)
  {
    cout << " " << var;
  }
  cout << endl;
 
 
  cout << "a[5] = { 1, 2, 3, 4, 5 }" << endl;
  cout << "array[begin_pos:end_pos]: c = array[1,2] " << endl;
  // array[begin_pos:end_pos]
  int begin_pos = 1;
  int subLen = sizeof(c) / sizeof(c[0]);
  int end_pos = begin_pos + subLen;
  copy_if(a + begin_pos, a + end_pos, c, [](int v){return true; });
  
  for each (int var in c)
  {
    cout << " " << var ;
  }
  cout << endl;
运行结果:    
a[5] = { 1, 2, 3, 4, 5 }
array[:end_pos]: b = array[:3]
 1 2 3
a[5] = { 1, 2, 3, 4, 5 }
array[begin_pos:end_pos]: c = array[1,2]
 2 3

很粗浅的分别实现下这几个点,体会是C++很强大,Golang更纯粹,少即是多。

10. 多参数返回

还记得在C里面为了回馈多个参数,不得不开辟几段指针传到目标函数中让其操作么?在Go里面这是完全不必要的。而且多参数的支持让Go无需使用繁琐的exceptions体系,一个函数可以返回期待的返回值加上error,调用函数后立刻处理错误信息,清晰明了。

11. Array,slice,map等内置基本数据结构

如果你习惯了Python中简洁的list和dict操作,在Golang中,你不会感到孤单。一切都是那么熟悉,而且更加高效。如果你是C++程序员,你会发现你又找到了STL的vector 和 map这对朋友。

12. Interface

Golang最让人赞叹不易的特性,就是interface的设计。任何数据结构,只要实现了interface所定义的函数,自动就implement了这个interface,没有像Java那样冗长的class申明,提供了灵活太多的设计度和OO抽象度,让你的代码也非常干净。千万不要以为你习惯了Java那种一条一条加implements的方式,感觉还行,等接口的设计越来越复杂的时候,无数Bug正在后面等着你。

同时,正因为如此,Golang的interface可以用来表示任何generic的东西,比如一个空的interface,可以是string可以是int,可以是任何数据类型,因为这些数据类型都不需要实现任何函数,自然就满足空interface的定义了。加上Golang的type assertion,可以提供一般动态语言才有的duck typing特性, 而仍然能在compile中捕捉明显的错误。

13. OO

Golang本质上不是面向对象语言,它还是过程化的。但是,在Golang中, 你可以很轻易的做大部分你在别的OO语言中能做的事,用更简单清晰的逻辑。是的,在这里,不需要class,仍然可以继承,仍然可以多态,但是速度却快得多。因为本质上,OO在Golang中,就是普通的struct操作

14. Goroutine

这个几乎算是Golang的招牌特性之一了,我也不想多提。如果你完全不了解Goroutine,那么你只需要知道,这玩意是超级轻量级的类似线程的东西,但通过它,你不需要复杂的线程操作锁操作,不需要care调度,就能玩转基本的并行程序。在Golang里,触发一个routine和erlang spawn一样简单。基本上要掌握Golang,以Goroutine和channel为核心的内存模型是必须要懂的。不过请放心,真的非常简单。

15. 更多现代的特性

和C比较,Golang完全就是一门现代化语言,原生支持的Unicode, garbage collection, Closures(是的,和functional programming language类似), function是first class object,等等等等。


有疑问加站长微信联系(非本文作者)

本文来自:博客园

感谢作者:cenglinjinran

查看原文:C、C++、Java、go的语法区别

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2534 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传