鸿 网 互 联 www.68idc.cn

当前位置 : 服务器租用 > 编程语言开发 > delphi > >

如何设计一门语言(三)——什么是坑(面向对象和异常处理)

来源:互联网 作者:佚名 时间:2013-05-06 23:43
在所有的文字之前,我需要强调一下,我本人对structure typing持反对态度,所以就算文中的内容看起来很像go的interface,读者们也最好不要觉得我是在赞扬go的interface。我比较喜欢的是haskell和rust的那种手法。可惜rust跟go一样恨不得把所有的单词都缩成最

在所有的文字之前,我需要强调一下,我本人对structure typing持反对态度,所以就算文中的内容“看起来很像”go的interface,读者们也最好不要觉得我是在赞扬go的interface。我比较喜欢的是haskell和rust的那种手法。可惜rust跟go一样恨不得把所有的单词都缩成最短,结果代码写出来连可读性都没有了,单词都变成了符号。如果rust把那乱七八糟的指针设计和go的那种屎缩写一起干掉的话,我一定会很喜欢rust的。同理,COM这个东西设计得真是太他妈正确了,简直就是学习面向对象手法的最佳范例,可惜COM在C++下面操作起来有点傻逼,于是很多人看见这个东西就呵呵呵了。

上一篇文章说这次要写类成员函数和lambda的东西,不过我想了想,还是先把OO放前面,这样顺序才对。

我记得我在读中学的时候经常听到的宣传,是面向对象的做法非常符合人类的思维习惯,所以人人喜欢,大行其道,有助于写出鲁棒性强的程序。如今已经过了十几年了,我发现网上再也没有这样的言论了,但是也没有跟反C++的浪潮一样拼命说面向对象这里不好那里不好要废除——明显人们还是觉得带面向对象的语言用起来还是比较爽的,不然也就没有那么多人去研究,一个特别合用来写functional programming的语言——javascript——是如何可以“模拟”面向对象语言里面的常用操作——new、继承和虚函数覆盖了。

所以像面向对象这种定义特别简单的东西,语法上应该做不出什么坑的了。那今天的坑是什么呢?答案:是人。

动态类型语言里面的面向对象说实话我也不知道究竟好在哪里,对于这种语言那来讲,只要做好functional programming的那部分,剩下的OO究竟要不要,纯粹是一个语法糖的问题。在动态类型语言里面,一个类和一个lambda expression的差别其实不大。

那么静态类型语言里面的面向对象要怎么看待呢?首先我们要想到的一个是,凡是面向对象的语言都支持interface。C++虽然没有直接支持,但是他有多重继承,我们只需要写出一个纯虚类出来,就可以当interface用了。

在这里我不得不说一下C++的纯虚类和interface的这个东西。假设一下我们有下面的C#代码:

interface IButton{} interface ISelectableButton : IButton{} interface IDropdownButton : IButton{} class CheckBox : ISelectableButton{} class MyPowerfulButton : CheckBox, IDropdownButton { // 在这里我们只需要实现IDropdownButton里面比IButton多出来的那部分函数就够了。 }

我们先不管GUI是不是真的能这么写,我们就看看这个继承关系就好了。这是一个简单到不能再简单的例子。意思就是我有两种button的接口,我从一个实现里面扩展出一个支持另一种button接口的东西。但是大家都知道,我那个完美的GacUI用的是C++,那么在C++下面会遇到什么问题呢:

#region 抱怨

一般来说在C++里面用纯虚类来代替interface的时候,我们继承一个interface用的都是virtual继承。为什么呢?看上面那个例子,ISelectableButton继承自IButton,IDropdownButton继承自IButton。那么当你写一个MyPowerfulButton的时候,你希望那两个接口里面各自的IButton是不一样的东西吗?这当然不是。那如何让两个接口的IButton指向的是同一个东西呢?当然就是用virtual继承了。

好了,现在我们有CheckBox这个实现了ISelectableButton(带IButton)的类了,然后我们开始写MyPowerfulButton。会发生什么事情呢?

猜错了!答案是,其实我们可以写,但是Visual C++(gcc什么的你们自己玩玩就好了)会给我们一个warning,大意就是你IDropdownButton里面的IButton被CheckBox给覆盖了,再说抽象一点就是一个父类覆盖了另一个父类的虚函数。这跟virtual继承是没关系的,你怎么继承都会出这个问题。

但这其实也怪不了编译器,本来在其他情况下,虚函数这么覆盖自然是不好的,谁让C++没有interface这个概念呢。但是GUI经常会碰到这种东西,所以我只好无可奈何地在这些地方用#pragma来supress掉这个warning,反正我知道我自己在干什么。

C++没有interface的抱怨到这里就完了,但是virtual继承的事情到这里还没完。我再举一个例子:

class A { private: int i; public: A(int _i)i:(_i){} }; class B : public virtual A { public: B(int _i):A(_i){} }; class C : public virtual A { public: C(int _i):A(_i){} }; class D : public B, public C { public: D():B(1), C(2){} };

大家都是知道什么是virtual继承的,就是像上面这个例子,D里面只有一个A对象,B和C在D里面共享了A。那么,我们给B和C用了不同的参数来构造,难道一个A对象可以用不同的参数构造两次吗,还是说编译器帮我们随便挑了一个?

呵呵呵呵呵呵呵呵,我觉得C++的virtual继承就是这里非常反直觉——但是它的解决方法是合理的。反正C++编译器也不知道究竟要让B还是C来初始化A,所以你为了让Visual C++编译通过,你需要做的事情是:

D() : A(0) // 参数当然是胡扯的,我只是想说,香港空间,你在D里面需要显式地给A构造函数的参数 , B(1) , C(2) { }

#endregion

大家估计就又开始吵了,C++干嘛要支持多重继承和virtual继承这两个傻逼东西呢?我在想,对于一个没有内建interface机制的语言,你要是没有多重继承和virtual继承,那用起来就跟傻逼一样,根本发挥不了静态类型语言的优势——让interface当contract。当然,我基本上用多重继承和virtual继承也是用来代替interface的,不会用来做羞耻play的。

当我们在程序里面拿到一个interface也好,拿到一个class也好,究竟这代表了一种什么样的精神呢?interface和class的功能其实是很相似的

网友评论
<