这篇文章的主要目的在于比较然后更加了解这两种语言的特点,属于读书笔记的一种类型,如果有什么不足、不对的地方,希望能够被指出来,谢谢能够指点。


  • 变量初始化
  • C
  • Java
  • 默认初始化
  • 复合类型
  • 复合类型的声明
  • 自定义数据结构
  • 指针和多维数组
  • 函数
  • 可变参数函数
  • 返回值
  • 类内函数定义


变量初始化

C++

int a = 0; int a = {0}; int a{0}; int a(0);

对于内置类型变量使用初始化列表,如果初始值存在信息丢失的风险,则编译器会报错。例如

long double ld = 3.1415926; int a{ld},b = {ld}; int c(ld),d = ld;

对于用花括号初始化的a和b编译器会报错。

Java

int[] a = new int[]{1,2,3,4,5};
int[] a = {1,2,3,4,5}

Java中含有以上两种的初始化方式。

默认初始化

如果内置类型的变量未被显示初始化,它的值由定义的位置决定。定义位于任何函数体之外的变量被初始化为0。定义在函数体内部的内置类型变量将不被初始化。这一点对于JavaC++ 都是一样的。但是Java会在编译阶段就报出错误,而C++仅仅会给出一个warning,然后在链接运行阶段报错。

但是在C++中,如果一个局部变量是一个非内置类型,则会被默认初始化。但是Java在局部变量环境中声明一个类对象的时候,如果没有new,则也会被编译器在编译阶段就提示使用了一个没有定义的引用,产生错误,如果不是局部变量,则会被默认定义为null。这是因为Java中所有的非基本类型都是类,然后局部变量仅仅是引用而已,引用在类中默认初始化为null,局部变量只声明不定义会产生错误。

复合类型

所谓的复合类型是指基于其他类型定义的类型,例如引用和指针。

在C++中,含有指针和引用的定义;对于Java来说,除了所有基本类型的其他所有需要new的类型,定义的指向new的内容就都是引用。

int[] i = new int[10]这就是Java的声明、定义一个变量的方式,其中的i就是一个引用。

复合类型的声明

C++中引用和指针的结合使用会使得声明形式复杂难懂,当面对一个复杂的指针或引用的声明语句时,从右向左阅读有益于弄清真实含义。例如int *& r 就可以看做是一个引用,它是一个指针的引用。(从右向左阅读)

更加复杂的对象就是增加了const限定符的声明。const限定符和Java中的final限制符类似,能够将一个变量定义为一个无法改变的对象,在C++中,使用const限制的变量会被编译器在相应的所有位置进行值替换,但是对于Java来说,final变量并没有这个特性。

引用的值本身就是无法改变的,如果i是a的引用,那么i就不会是b的引用了。如果用const来修饰引用,那么认为不能通过这个引用来更改引用对象产生的值。 const int &rl = ci 表示不能通过rl来改变ci的值,即rl = 1这样的赋值语句是会产生错误的。

指针和引用的结合就看const这个修饰符是在*号前面还是*后面,例如const int * p就表示p是一个指向常数的指针,不能通过p来修改实际指向的内容(\*p = 1就是错误的),但是int \* const p就表示p是一个常数指针,这个指针指向的对象不能被改变(p = &b就是错误的)

自定义数据结构

在Java中,除了基本类型之外的内容都是对象。C++中也有类的定义,用class和struct来声明,在class中,默认的访问限制是private,在struct中,默认是public。但是在Java中,Java类的默认访问是包访问,包访问是一种建立在private和protect之间的一种访问权限。

在《Thinking in Java 4th edition》这本书中,说Java的类可以有类内初始值,但是对于C++来说这个值是无法存在的,在C++11中这个内容被增加了,每个C++的类都可以为数据成员提供一个类内初始值,没有初始值的成员将被默认初始化。

指针和多维数组

int (*p)[4];表示的是p指向含有4个整数的数组

int *ip[4];表示的是整型指针的数组

const int &b = 42这是正确的。能够使用字面值初始化一个常量引用。

函数

可变参数函数

C++中的可变参数函数的声明一般如下:

int sum(int count,...){}

C++还有另一种可变参数的声明方法:使用initializer_list对象来进行声明:

int sum(initializer_list<int> il){}

Java中的可变参数的函数的声明一般如下:

int sum(int ... args){}

在Java中可以把可变长参数看成是一个参数数组,可以通过遍历的方式得到所有的参数形式。但是在C++的第一种形式中,可变长参数需要通过stdarg.h头文件中定义的内容来进行处理:

int sum(int count,...){
    va_list args;
    va_start(args,count);
    while(count--){
        sum+=var_arg(args,int);
    }
    va_end(args);
    return sum;
}

上面代码段就是演示在C++的第一种情况中如何使用可变长参数列表。在C++的第二种情况中,il就直接是一个list,直接使用迭代器即可。

返回值

在C++中,返回只能是值而不能是引用,可以是指针但是指针不能指向在程序中不用new产生的内容,因为局部变量在函数返回之后会被销毁,所以栈上的内存会没有任何意义。如果直接是值的返回,C++会相当于产生一个新的内容,此间会发生拷贝构造函数或者拷贝函数的调用过程。如果有编译器优化,编译器可能会将拷贝构造的过程取消,直接在外面新建一个对象,通过引用传入函数,然后函数的返回改成void。在Java中,返回值如果是基本类型,则并没有关系直接拷贝一份返回,如果是非基本类型,因为Java产生的对象全都是new出来的,所以也没有关系。不过编译器也可能会做一些编译优化。

编译优化过程如下:

C++

class A{}
A calc(int a,int b){
    A a;
    return a;
}
//上述代码会产生拷贝构造过程
A* calc(int a){
    A a;
    A *ret = &a;
    return ret;
}
//上述代码可以通过编译,但是在运行时会产生错误

类内函数定义

C++

在C++中可以在类成员函数的定义中加上const表示该成员函数不能更改类中的属性的值。

class A{
    int a = 0;
    int getA() const{return a}
    //int getA() const{ a = 1;return a} //出错,因为不能更改类成员的值
    int getA(int){ a = 1;return a}
}
A a;
a.getA();
a.getA(2);
const A b;
b.getA();
b.getA(2);  //出错,因为b是const的,保证了b不会更改类对象,但是getA(int)是非const的,无法保证。

同时在C++中const关键词在类前是无意义的。一般用来修饰方法(放在类方法后)、变量(常量、常量指针、指向常量的指针、常量引用等)

Java

在Java中没有类似的限制成员函数无法更改类属性的关键词,与const相似的关键词有final,final关键词可以修饰变量,这时候的意义和const关键词的意义是一致的,都表示关键词无法更改;修饰方法表示该方法足够完整,不能被子类所重写,同时该方法在编译的时候会直接静态表绑定;final类表示该类是完整的,无法被子类所继承。

总的来说,final的好处有:

  1. final关键字提高了性能。JVM和Java应用都会缓存final变量。
  2. final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。
  3. 使用final关键字,JVM会对方法、变量及类进行优化。