说明:本文章纯属个人观点,不保证绝对正确,欢迎大家批评和指正,同时我自己也会对本文不断的更新和完善。
引言:
本文自工作以来使用过C++、Java、Python、Groovy、Objective C、Lisp,我最初学的就C++的,当时对她情有独钟,灵活和强大充分体现了她的智慧,随着你对她理解的深入,你会发现随便一段代码会有很大的优化余地,以致于大牛和小菜写的代码有很大的区别,举个十常简单的例子,串拷贝大家都熟悉吧(至少是一道常考的面试题,我也经常拿此考考应届生
):
数据结构书上的最终版是这样的(注意:本文所有代码都不是商用版本,为了简洁,没注意风格的良好性):
void strcpy(char* des, const *char src)
{
while(*des++ = *src++);
}
最原始版本是这样写的:
void strcpy(char* des, const char* src)
{
while(*src != '\0')
{
*des = *src
des++
src++;
}
}
当然,中间还是许多进化版本,不是一步跳过来的,具体请参考谭浩强的《数据结构》,这里只是说明同样的功能,可以充分利用C++语言灵活语法写出非常精炼的代码,这里利用到的语言特性有:操作符优先级、结束符'\0'等价于0或NULL、非0即为真。这只是个简单的例子,如果应用类指针、泛型、虚继承等特性,就可以在大项目中做出很多优化,使得代码精炼且高效。
(题外话:有很多书和文章出于易读性原则,不鼓励第一种风格,我觉得没那么糟糕,如果是常用的,人人都熟练某一段,这样写就相当于单词缩写,就好比设计模式一样是一种共同语言,你一说关键字,人家立刻就明白这一整块代码)
总之,C++给我的感觉就是:只有想不到,没有做不到。
但是当我学过其它语言之后,发现C++并不是我想像中的完美,每种语言都有自己独到的智慧,虽然并不是每一种我都熟悉,但她们的设计思想让我受益匪浅。如果说C++灵活,那世上还有另一种灵活,经过几年工作的提炼,我发现C++有这些不方便之处。
以下是我当前想到的:
.头文件:
这是我发现的每一个不方便的地方,这件事由来还挺曲折离奇的,我学的第二种语言是Java,当时是为了开发ZTE第一个Android的机顶盒,当时那里只有一两个人懂Android,而且马上就要离职了,所以就只有赶鸭子上架了,叫我去学Java,开发Android应用,我当时还是唯C++独尊,带着鄙视Java的情绪去学习Java,第一天,写了个HelloWorld,发现Java竟然没有头文件,当时心里就想这是什么扯淡语言,头文件都没有,那我怎么提供接口给别人还不用暴露我的实现。但后来,随着学习的深入,发现Java根本就不需要头文件,它的接口信息都包含在.class里了,甚至还会包含接口的注释。
不用头文件,编程时显示要方便一点,改函数名的的时候不用改两个文件了,因为那显然有冗余的工作量,即然是冗余就应该让机器去做,不要耽误人的时间。所以我觉得可以在C编译成的*.o文件前面部分放入接口信息,这样别人引用可以像Java或Objective C或Python这样import了。
.函数必须先申明再调用:
【注意这里说的不是变量的先申明再使用】这个规矩相当难受,其实只要编译器搜索一遍本文件,就可以找出写在后面的函数了,这样只是耽误一点编译时间,即不会影响运行效率,还可以使人少写些代码并减少人放错的概率,何乐而不为呢?
.成员变量申明时不能初始化:
我想多数人学C++时,都会自然而然的认为成员变量申明的时候就可以给他赋个初始值,像这样:
// file1.cpp
class Foo
{
int bar = 0; //wrong in C++, but legal in Java
}
在C++是允许这样的,按照官方的说法,申明文件是属于类的而不是属于对象的,不管他怎么说,Java也可以申明时定义,我觉得这样很方便,也符合人的思考习惯,其实你可以当成初始化值来理解,无论如何,这条规定来了不便。
.泛型语法不够简洁:
同是泛型,Java的语法就比C++要简洁的多,一个单一泛型方法在C++中是这样表示:
template
int compare(const T &v1, const T &v2)
{
// ...
}
而在Java中是这样:
static int compare(T v1, T v2) {
// ...
}
(用static是因为Java没有单独的函数,所以方法必须写进类,static方法更C++的函数最像),一个泛型类在C++中这样的:
template
class Foo
{
public:
void bar(T arg1);
// ...
};
而在Java中是这样的:
class Foo {
public void bar(T arg1){};
}
显然,C++多了个关键字template和typename,而Java只要一对尖括号就可以了,更易记忆,更简洁,写代码也方便。有可能因为Java来源于C++,Java创造者意识到这一点,才改进的。所以理论上C++也可以支持这样的语法,应该改一下编译器就可以了。
.标准库不强大:
相比Java的标准库,C++的STL实在是太薄弱了,就几个容器类,什么网络编程,加密算法都没有,全部都得到网上找开源的库,同时使得人人都要造车轮,大大的加长了开发周期,更延缓了C++自身的发展。
.API注释:
Java的.class文件里已包含了注释,使得在集成开发环境里,可以边编程边查看API说明,如下图:
我觉得C++也可以把注释打包到.o和.so文件里,这样别人用你的库的时候就不会找你要文档(你还很有可能没写)或问题你怎么用了,当然这个功能应该可配置,以满足一些担心库被泄漏的用户。
.做Linux开发没有一个好用的集成开发环境:
这个虽然跟C++语言本身没有多大联系,但这可能是做C++的工程师的一块痛处,这条可能会有很多人反对,有人会觉得可以通过配置vim或emacs(这个东西不一定好使,却是一个很好的“装逼”利器,我也研究过,的确很有面子,特别是在新手MM面前)来使用强大的功能,配置复杂不说(有一个哥们用的emacs有3000行的配置文件,不过那人是兴趣原因),也不是很好用,显然没有window上的VS和Eclipse开发Java那么智能,用Eclipse开发Java从来都没用担心编译问题,在C++上可能只通过边写边手动频繁的办法来减少编译问题带来的痛苦。还有人会觉得集成开发环境不好,会使得程序员对编译细节和程序的生成过程全然不知,这个我以前也这么认为,还专门学习研究编译和可执行文件,还绞尽脑汁看过龙书(Alfred V. Aho写的编译原理),但后来发现没什么用处,如何不是写编译器,那些知识很难派上用场,久而久之,我都忘了。所以我认为,程序员应该有更多的时间去思考软件的设计和架构,而不是那些简单又繁琐的编译工作,更不应该每天去重复做这些事情,应尽可能让机器去做。比如说自动完成功能可以大大提高输入效率;自动显示API注释功能使得不需要先查文档才知道使用;写注释也很方便,只需在方法前输入“/**”再输入回车,就会自动完成API注释结构,包括参数名,如下图:
边写边后台编译的使得再不用担心编译错误,还有错误提示并给出建议方案,如下图:
函数未返回值给出的建议,如果你点击第一个选项,就会自动帮你加上一行"return 0;",下图到达不了的代码两个示例,有提示
还有重构功能,在Eclipse中重构很简单,比如你改一个函数名,他会自动帮你更改所有调用处,包括其它的工程,这个功能苹果家的XCode也有,你用C++估计得使用一番Ctrl+F、Ctrl+C、Ctrl+V了,如果第二天又想改回来,你又得痛苦了。除此之后还有其它很多强大的功能,在此就不一一列举了。
.匈牙利命名法:
这个好像也跟C++关系不大,不过实际上也是由于IDE不强大造成的,我想有人希望看到这种命名法写的代码,没人喜欢写这种命名法的代码,特别是有时想改某个变量的类型时,如int型改char型,这个时候你就得一个个改了,其实本质是要看到类型信息,看看强大的IDE是怎么做的:
在鼠标悬停的时候自动显示了,类型信息根本就不要在变量名上体现,我还听说这种命名法是微软发明,我在想他微软为何不直接改一下VS的代码增加这个功能而去增加他千千万万的员工的工作量呢,更想不通的是为何还流行了这么多年,难道没人发现这个问题吗,还是我想错啦。
说明:本文章纯属个人观点,不保证绝对正确,欢迎大家批评和指正,同时我自己也会对本文不断的更新和完善。