面向对象语言java与c++的比较

面向对象的思想

面向对象的主要思想是提供了类与对象的概念。类是对象的抽象,也可以说类是对象一种描述。对象是类的实例。高级编程语言通过面向对象思想让程序更符合人对真实世界的认识,让程序更容易让人理解并且更容易描述事物。

面向对象三大特性

面向对象的三大特性是封装、继承、多态。封装可以屏蔽对象的属性和实现细节,让程序更关注业务本身的逻辑,进而更容易的开发出大型的软件。继承可避免重复的做轮子,什么工作都从头开始做。也让描述变得简单,例如猫与白猫,把白猫的描述成一只白色的猫,而不是一种白色的有四只腿、两个眼睛、一张嘴...的动物。也更符合人们的日常表达。多态主要是提供行为可变的一种能力。也就是接口(方法)的不同实现。多态分为编译时多态和运行时多态。编译时多态就是 Overload机制,通过函数签名(函数名,函数参数类型、个数、顺序相同,才表示签名相同)确定其唯一性,让同名称的函数有了多种形态。运行时多态就是Override机制,子类能重写父类的方法。让运行时决定调用哪个方法,这样就可以让运行时方法有不同的实现。

java与c++三大特性的比较

封装性

  • c++是站在c语言角度设计的面向对象语言,class也是从struct演变来的,struct默认的访问权限是public的,而class默认的访问权限是private的,它们都支持程序员设置有不同的访问权限:private、protected、public。这个设计思想是封装数据,隐藏细节,但是c++又提供了friend函数,友元可以访问类的任何成员,破坏了封装。也就是它的设计思想是封装,但是又提供了破坏这一思想的接口,这就是出现了矛盾。所以java设计丢弃了friend。

  • c++是面向对象早期的发明的高级语言,其数据成员的思想设计是来自于人对真实世界的认识。现实生活中,类作为对象一种描述,其数据成员是对对象才有意义,所以在类中,对数据成员赋初值是没有意义的,故不支持类对数据成员赋初值。对非静态数据成员赋初值,会出现如下情况:

class Point {
      private:
          float z = 10;
}

warning: non-static data member initializers only available with -std=c++11 or -std=gnu++11 [enabled by default]
   float z = 10;

也就是说,只有c++11才支持类给非静态数据成员赋一个默认值。否则会给出一个警告。

对静态数据成员赋初值,只需要修改成static float z = 10f;会出现编译错误:

error: ISO C++ forbids in-class initialization of non-const static member ‘Point::z’
   static float z = 10

虽然类的静态数据成员属于类,但是也不能在类中赋默认值。这是因为如果在类中赋默认值无法确定此变量的作用域。

当然修改成static const float z = 10;是可以编译过的.对于编译器来说,只有const static的数据成员才会被识别为常量。

java没有以上限制,给数据成员赋默认值,主要是为了方便定制一个默认对象,申明一个类,直接new就可以得到一个默认对象,这个默认对象默认属性是可以在申明类的时候定制的。而不像c++,只能让编译器给类赋一个默认值,如果编译器调用自己添加的构造函数,一般为0, 如果程序员自己定义了构造函数,值不确定,如下:

#include
using namespace std;

class Point {
    private:
        float x,y;
    public:
        void show() {
            cout << "x = " << x << ",y = " << y << endl;
        }
};

int main() {
    Point point = Point();
    point.show();
    return 0;
}

运行结果如下:
x = 0,y = 0

如果给Point添加一个构造函数,Point() {};
运行结果如下:
x = 2.93789e+22,y = 4.59163e-41

对属于类的静态数据成员,java也是可以直接赋初值的,这是因为java类需要被类加载才能在jvm上运行,它的静态数据成员在被加载的那一时刻正好可以确定作用域。

  • const可以作用于类成员,包括数据成员和成员函数。表示不变的意思。但对const成员初始化和用法,规定比较多。

    1. const的非静态数据成员只能在初始化列表中初始化,这个操作特殊.
    2. 如果申明一个const对象,只能调用对象的const方法。这操作也太奇葩了.
    3. 如果一个函数签名一样,再申明一个同签名的函数,居然能编译过。根据重载(overload)语义,这不是重载啊。这操作挑战认知。

个人感觉C++ const就是为兼容C的const,强制定义类与对象的const属性。所以java没有再使用const表示不变的意思,而是使用final,最终的。最终的类不能被继承,最终的方法不能被复写,最终的变量不能被修改。总之就是一个意思,最终的就是不可被改写。

继承性

对于继承,c++是支持多继承的,非常强大,但是容易出现方法二义性,这是不允许的。其实客观世界也就是两种繁殖方式:无性繁殖和有性繁殖。无性繁殖是产生的后代只有一个亲体,也就是后代只有一个parent。有性繁殖也只不过两个。c++这种多继承并不是只有两个,这个设计并不符合客观现实。如果处理不好会让代码耦合性更大,逻辑更复杂,关系更紊乱。并且会出现单个类设计和逻辑都是正常的,放在一起派生的子类就产生了二义性。java设计放弃了多重继承,采用无性繁殖的模式,一个顶层祖先Object类,派生类通过tree的模型关联在一起,因为类的parent只有一个,提供 了super指针,可以通过它回溯找到所有的祖先。并通过接口实现类的额外功能,可以理解为无性繁殖的基因突变。

  • 子类能隐藏父类同名普通成员,包括数据成员和成员函数。这个是隐藏不是重写。这个概念来自于C的变量作用域。内部作用域的变量隐藏外部作用域的同名变量。不是同名的普通成员,子类可以继承,这个继承是打引号的,本质上是子类能访问父类作用域内可以访问的成员。也就是说父类的作用域大于子类。C++的父子类的同签名成员函数为什么也要遵守C的变量作用域规则?我个人认为这是个设计缺陷,既然子类定义了同签名函数隐含的意思就是要重写,如果不重写,干嘛要定义一个同签名的函数。所以java优化这个设计,同名数据成员,子类隐藏父类的,同签名函数,子类重写父类的。

  • 把子类对象赋值给父类指针

在C++中,把一个子类赋值给父类指针,然后调用一个普通成员函数,调用的是父类的成员函数函数,如果父类没有实现这个成员函数,则报错。一句话普通成员函数不支持动态多态性。

说到多态,对于编译时多态,java和c++一样。但是运行时多态,java不需要virtual函数支持,子类可以Override父类成员方法。更符合实际情况,如果子类和父类的成员方法相同,对于程序设计来说,目的就是为Override。如果目的不是Override,干什么要写一个相同的成员方法呢?c++这种通过virtual关键字,就不方便了,在基类设计的时候,就要指定需要override的方法。这虽然可以支持运行时多态,但是支持多态的接口是一定的,显然限定了运行时多态的扩展性。例如:如果派生类需要Override基类的接口,如果基类中的此接口没有申明为virtual,还要修改基类的接口变成virtual,同时继承此基类的其它的接口,要实现此接口。

你可能感兴趣的:(面向对象语言java与c++的比较)