C++多重继承

        封装、继承和多态是面向对象程序设计的三大特性。如今有非常多的语言支持面向对象编程,已经充分说明了面向对象思想的价值。C++是一门经典的面向对象程序设计语言,但是为了提供对C语言的向下兼容性,C++也支持面向过程编程,因此C++不是一门“纯粹” 的面向对象语言。据我所知,Java似乎是“纯粹”面向对象的,一切操作都必须封装到类里,连main()函数也是,所有类都直接或间接地由Object类派生而来。但也有很多脚本语言,例如PHP和Python,既支持面向对象,也支持面向过程。

        C++一直因它的庞大、复杂、迟缓为人所诟病。在我目前所知的语言里,以C系语言为主,汇编另说,Lisp及其变种的语法和结构与C系语言不同,C++确实算得上是最复杂的编程语言了。一方面,C++与C甚至汇编类似,许多操作并没有现成的库或模版可循,必须要自己实现,而我所见的其他更高级(请注意,低级与高级用得是其在计算机专业术语中的含义,指在计算机层次模型中所处的地位,而不是通常意义上的低级与高级)的语言,例如Java、Python等,它们都提供了非常丰富、成熟且方便使用的库,用它们写一个网络爬虫或图形界面,在我的认识中,应该是要比汇编、C和C++要简单一些(其实我对它们的库都不熟),很多时候只要调用现成的函数或模版,也就能实现基本功能了;另一方面,C++为了保证灵活性与效率,只做一些基本的工作,而将大部分选择权交给程序员,程序员必须自己斟酌各种可能性,保证代码的正确,而不是由编译器或运行环境来代劳。比如,C++的许多函数(包括从C继承来的那部分)只负责实现功能,程序员需要自己检测返回值或某些变量来判断是否成功执行,异常机制也必须显式调用,而Java的JVM却时刻在监测程序的运行,检测其抛出的异常。因此,与C相比,C++庞大迟缓;与Java等相比,C++复杂、不安全。

        顺便来说说计算机的层次模型。其他学科我不了解,但我感觉计算机科学的最大特点就是分层。首先网络分层模型大家都耳熟能详了,应用层传输层网络层等等;而在更宏观的角度,可以将计算机的不同层次看成一个个虚拟机。这个虚拟机不是平常说的用软件虚拟一个操作系统,在组成原理的相关教材中有所说明。通俗地说,我们平常看电视玩电脑,我们看到的是什么?是这些机器展现给我们的“界面”或者说”接口“,我们只需要按一些按钮或鼠标,而不需要知道它们的原理。而对于专业人员来说,他们”看“到的和我们不同,他们看到是其中各种器件、电路等。因此,在不同层次上,同一个东西展现出的样貌是不同的。对于程序员来说,最上面一层是某种编程语言的界面,我们要查看语言的手册,看看它为我们提供了什么样的接口,用什么语句或函数可以做什么事情。往下一层,我们要知道,这些语句和函数究竟做了什么事情以及怎么做的,会产生什么样的后果等等;再往下一层,就是运行环境和操作系统;再下面还有体系结构、指令集等等。程序员一般不需要知道最底层的电路方面的事情,只需要知道其特性和接口。在不同层面上编程是不太一样的,例如,用汇编语言写程序,需要自己操作寄存器等,而在高级语言中,这些都是透明的。用汇编语言写图形界面,现成的库和函数不多,一个最简单的图形界面可能也要上百行代码,而用高级语言,可能主要就几行语句。同样的代码量,高级语言完成的功能要多于用低级语言;同时,高级语言的难度要低于低级语言,同样的代码量,用低级语言,需要知道很多知识,而用高级语言可能只是看看库、类和函数的接口。写一百行Java代码和一百行汇编代码一般来说难度是不同的,更何况Java完成的功能更多。理论上,汇编可以做任何事情,其他语言(也许除了机器语言)能做到的,汇编都能做到。但是用Java、PHP写个网站很常见,用汇编来做个网站我还真没听说过。

        分层模型的最大好处是可以屏蔽无关细节,在高层工作时不必理会低层的实现细节,只要知道相应的接口就行了。应用程序员无须知道操作系统及硬件的工作原理也能编写出程序,因而不经过高等教育或者经过少量的高等教育就可以直接学习和培训编程方面的技能,于是就可以(部分)解释为什么如今有不少计算机技能培训学校了,它们也还是有一定效果的,毕竟程序员对大多数人来说也只是一份谋生的职业,技术什么的够用就好,就像不是每一个物理学家都需要会造原子弹~

        好了,一不小心又长篇大论了。现在开始进入正题。。

       我对多重继承并没有什么深入学习和研究,多重继承在语义和逻辑上究竟意味着什么,有没有什么缺陷我不清楚。但多重继承使用难度大却是公认的,以至于在C++之后面向对象语言如Java取消了多重继承,以接口代替。根据我所查阅的各种资料如《C++程序设计语言》《Effective C++》《C++编程思想》,其中都有专门的章节讨论多重继承,多重继承似乎没有什么明显缺陷,无非是使逻辑更复杂些,开销大一些,但同时也能活得灵活性等好处。目前来说,C++的多重继承是一个难点而不是重点,多重继承并不是C++的重点特性,甚至已经被边缘化了。多重继承的功能可以用组合等代替,避免其复杂性,因此多重继承可以尽量少用甚至不用。

       多重继承面临的最大问题是歧义性问题。在不同的基类中拥有同名函数和属性,访问时就须指明其所属类:

class A
{
	public:
		void show();
};

class B
{
	public:
		void show();
};

class C:public A, public B
{
};

void f()
{
	C *c = new C();
	c->show(); // 错误:歧义
	c->A::show(); //ok
	c->B::show(); //ok
}


这样看起来就比较混乱,Bjarne建议在派生类里定义新函数覆盖基类函数再在新函数里调用基类的同名函数。

还有另一种更麻烦的歧义性问题,即著名的菱形继承。

midClass1和midClass2都继承了baseClass,derivedClass继承midClass和midClass2后会保存两份baseClass的副本,这样就带来了数据上的冗余和访问上的复杂性。解决的方法是采用虚基类(我觉得叫虚继承更贴切些?)

class midClass1:public virtual baseClass{};

class midClass2:public virtual baseClass{};

class derivedClass:public midClass1, public midClass2{};


       这样,在derivedClass中只有一个baseClass的副本。

       “如果虚基类或者由虚基类直接派生的类是抽象类,钻石继承将特别容易控制”(Bjarne),C++虽然并没有提供interface关键字,但这就是C++的灵活性了,C++并不强迫程序员使用接口,但程序员有选择使用接口的权力,只要将基类设计成只有方法没有属性的抽象类就行了。

 

以下是《Effective C++》中Scott Meyer对使用多重继承的忠告:

      很多东西复杂未必是因为其本身复杂,而是因为和其他东西结合起来才会复杂。我对多重继承的理解还很浅薄,当多重继承牵涉到多态等时会发生什么我并不清楚。

      灵活性、效率、安全性、复杂性等常常不能兼得,从我的感觉来看,C++处于C和Java之间,各方面都做得相对平衡,换言之,各方面都比较一般,没有C的简洁高效和Java的简单易用。C++的使用是把双刃剑,需要自己斟酌权衡。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值