如何设计一门编程语言(二) 什么是坑(b)2014-08-23我从来没有在别的语言的粉里面看见过这么容易展示人性丑陋一面的粉,就算是从十几年前开始的C++和C对喷,GC和非GC对喷,静态类型动态类型对喷的时候,甚至是云风出来喷C++黑得那么惊天动地的时候,都没有发生过这么脑残的事情。这种事情只发生在go语言的脑残粉的身上,这究竟代表什么呢?想学go语言的人最好小心一点了,学怎么用go没关系,go学成了因为受不了跳到别的语言去也没关系,就算是抖M很喜欢被折腾所以坚持用go也没关系,但是把自己学成了脑残粉,自己的心智发生不可逆转的变换,那就不好了。当然,上一篇文章最后那个例子应该是我还没说清楚,所以有些人有这种“加上一个虚析构函数就可以了”的错觉也是情有可原的。Base* base = new Derived;之后你去delete没问题,是因为析构函数你还可以声明成虚的。但是Base* base = new Derived[10];之后你去delete[]发生了问题,是因为Derived和Base的长度不一样,所以当你开始试图计算&base[1]的时候,你实际上是拿到了第一个Derived对象的中间的一个位置,根本不是第二个Derived。这个时候你在上面做各种操作(譬如调用析构函数),你连正确的this指针都拿不到,你再怎么虚也是没用的。不过VC++单纯做delete[]的话,在这种情况下是不会有问题的,我猜它内部不仅记录了数组的长度,还记录了每一个元素的尺寸。当然,你直接用bases[1]->DoSomething()的时候,出事是必须的。所以今天粉丝群在讨论昨天的这个例子的时候,我们的其中一位菊苣就说了一句话:当你使用C++的时候C的一部分子集最好少碰我也很赞同。反正C++已经有各种内置类型了,譬如typeid出来的按个东西(我给忘了)啊,initialization_list啊,range什么的。为什么就不给new T[x]创建一个类型呢?不过反正都已经成为现实了,没事就多用用vector和shared_ptr吧,不要想着什么自己new自己delete了。今天我们来讲一个稍微“高级”一点点的坑。这是我在工作之后遇到的一个现实的例子。当然,语言的坑都摆在那里,人往坑里面跳都肯定是因为自己知道的东西还不够多造成的。但是坑有三种,第一种是很明显的,只要遵守一些看起来很愚蠢但是却很有效的原则(譬如说if(1 == a)…)就可以去除的。第二种坑是因为你不知道一些高级的知识(譬如说lambda和变量揉在一起的生命周期的事情)从而跳坑的。第三种纯粹就是由于远见不够了——譬如说下面的例子。在春光明媚的一个早上,我接到了一个新任务,要跟另一个不是我们组的人一起写一个图像处理的pipeline的东西。这种pipeline的节点无非就是什么直方图啊,卷积啊,灰度还有取边缘什么的。于是第一天开会的时候,我拿到了一份spec,上面写好了他们设计好但是还没开始写的C++的interface(没错,就是那种就算只有一个实现也要用interface的那种流派),让我回去看一看,过几天跟他们一起把这个东西实现出来。当然,这些interface里面肯定会有矩阵:
template<typename T>class IMatrix{public:virtual ~IMatrix(){}virtual T* GetData()=0;virtual int GetRows()=0;virtual int GetColumns()=0;virtual int GetStride()=0;virtual T Get(int r, int c)=0;virtual void Set(int r, int c, T t)=0;};
其实说实话,IMatrix这么写的确没什么大问题。于是我们就很愉快的工作了几天,然后把这些纯粹跟数学有关的算法都完成了,然后就开始做卷积的事情了。卷积所需要的那一堆数字其实说白了他不是矩阵,但因为为这种东西专门做一个类也没意义,所以我们就用行列一样多的矩阵来当filter。一开始的接口定义成这个样子,因为IBitmap可能有不同的储存方法,所以如何做卷积其实只有IBitmap的实现自己才知道:
template<typename TChannel>class IBitmap{......virtual void Apply(IMatrix<float>& filter)=0;......};
于是我们又愉快的度过了几天,直到有一天有个人跳出来说:“Apply里面又不能修改filter,为什么不给他做成const的?”于是他给我们展示了他修改后的接口:
template<typename TChannel>class IBitmap{......virtual void Apply(IMatrix<const float>& filter)=0;......};