首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

被误解的C++——学习和使用,该怎么处理

2012-03-01 
被误解的C++——学习和使用学习和使用C++太复杂了。我猜想你一定是在计算机屏幕前不住地点头。其实我也在使劲

被误解的C++——学习和使用
学习和使用
C++太复杂了。我猜想你一定是在计算机屏幕前不住地点头。其实我也在使劲的点头。没错,C++着实复杂,大概是迄今为止最复杂的语言。于是,便产生了一个推论:C++难学难用。
这句话也相当准确,但并非所有时候都这样。C++的学习和使用可以分为若干个层面,这些层面由简到繁,由易到难,分别适合不同层次的学习者和使用者。
先让我们来看看C++究竟有多复杂。我大致列了个清单,包括C++的主要语言特性。基本的一些特性,象变量、条件、选择等等,都掠去了,主要集中在体现C++复杂性的方面:
1.指针。包括变量指针、函数指针等。可以计算。
2.引用。包括变量的引用、函数的引用等。
3.自由函数。
4.类。包括具体类和抽象类。
5.重载。包括函数重载和操作符重载。
6.成员数据。包括static和非static的。
7.成员函数。包括static和非static的。
8.虚函数。包括对虚函数的override。
9.继承。包括多继承、虚继承等。
10.多态。包括动多态和静多态。
11.类型转换。包括隐式的和显式的,以及臭名昭著的强制转换。
12.模板。包括类模板和函数模板。
13.模板特化。包括完全特化和部分特化。
14.非类型模板参数。
15.模板-模板参数。
我对照了C#的特性清单。C++比C#多的特性主要是:1、2、3、9的多继承、10的静多态、13、14、15。
而C#比C++多的特性主要有(这里没有考虑C++/CLI,C++/CLI补充了标准C++):
1.for   each关键字。
2.as、is关键字。
3.seal关键字。
4.abstract关键字。
5.override/new关键字。
6.using关键字的一些补充用法。
7.implicit/explicit关键字。(C++有explicit关键字,但只用于构造函数)
8.interface关键字。
9.property。
10.delegate。
我们来分析一下。C++比C#所多的特性集中在编程技术方面,特别是编程模式,如由模板带来泛型编程和元编程机能。在OOP方面,C++走得更远,更彻底,主要表现在多继承和操作符重载方面。在传统的编程技术上,C++区分了对象和对象的引用(指针和引用)。而C#所有引用对象所持的都是引用。最后,C++拥有自由函数。
反过来再看C#,C#所多的都是些关键字。这些关键字都集中在OOP方面,并且都是以使编程技术易于理解为目的的。很多人(包括我在内)都希望C++也拥有C#的这些关键字(至少部分)。但与一般人的理解不同,C++标准委员会实际上是被编译器开发商所把持着,他们对引入关键字尤其神经过敏。没有办法,现实总是有缺憾的。
从这些方面可以看出,C++在编程机制上远远多于C#(Java的机制甚至更少)。对于新入行的人而言,一口气吞下这些内容,足以把他们撑死。相反,C#增加的关键字有助于初学者理解代码的含义。这些就是C#和Java比C++易于学习(易于理解)的真正原因。
但是,必须要强调的是,C#和Java中易于学习和理解的是代码,而不是这些代码背后的技术原理和背景。
我看到过的绝大多数C#代码都充满了重复代码和大量switch操作分派。如果这些程序员充分利用C#和Java的OOP机制,这些严重的代码冗余可以消除一半。(如果C#和Java具备C++那样的泛型编程能力,则另一半也可以消除。)这些程序员都是在没有充分理解语言机制和OOP技术的情况下编写软件,事倍功半。
这种情况在C++也有发生,但相对少些。这大概是因为C++足够复杂,使得学习者产生了“不彻底理解C++就学不会C++,就用不了C++”的想法。这种想法有利有弊,利在于促使学习者充分理解语言和语言背后的技术,而弊在于它吓跑了很多人。实际上,我们一会儿就会看到,C++可以同C#和Java一样,可以在不理解其中原理的情况下,仅仅按照既定规则编程。当然我们不希望这样,这是不好的做法。但鉴于现在业界
的浮躁心态,我们也就入乡随俗吧。
注意了!下面这句话是最关键的,最重要的,也是被长期忽略的:C++之所以复杂,是为了使用起来更简单。听不明白!自相矛盾!胡说!别急,且听我慢慢道来。
(限于篇幅,我这里只给出最后一部分案例代码,完整的案例在我的blog里:)
有三个容器,c1、c2、c3。容器的类型和元素的类型都未知。要求写一个算法框架,把c1里的元素同c2里的元素进行某种运算,结果放到c3里。
由于容器类型未知,必须使用所有容器公共的接口。所以,我写下了如下的C#代码:
public   delegate   void   alg <T1,   T2,   R> (T1   v1,   T2   v2,   R   r);
public   static   void   Caculate_Contain <C1,   T1,   C2,   T2,   C3,   T3>
(C1   c1,   C2   c2,   C3   c3,   alg <T1,   T2,   T3>   a   )
where   C1:   IEnumerable <T1>
where   C2   :   IEnumerable <T2>
where   C3   :   IEnumerable <T3>
{
IEnumerator <T1>   eai1   =   c1.GetEnumerator();
IEnumerator <T2>   eai2   =   c2.GetEnumerator();
IEnumerator <T3>   eai3   =   c3.GetEnumerator();

while   (eai1.MoveNext()   &&   eai2.MoveNext()   &&   eai3.MoveNext())
{
a(eai1.Current,   eai2.Current,eai3.Current);
}
}
//使用
public   static   void   CaculThem(int   v1,   int   v2,int   r)   {
        r=v1*v2;
}
Caculate_Contain(ai1,   ai2,   ai3,   new   alg <int,   int,   int> (CaculThem));
public   static   void   CaculThem2(float   v1,   int   v2,double   r)   {
        r=v1*v2;
}
Caculate_Contain(af1,   ai2,   ad3,   new   alg <float,   int,   double> (CaculThem2));
我使用了一个委托,作为传递处理容器元素的算法的载体。使用时,用具体的算法创建委托的实例。但具体的算法CaculThem()必须同相应的容器元素类型一致。
下面轮到C++:
template <typename   C1,   typename   C2,   typename   C3,   typename   Alg>


Caculate_Container(const   C1&   c1,   const   C2&   c2,   C3&   c3,   Alg   a)
{
transform(c1.begin(),   c1.end(),   c2.begin(),   c3.begin(),   a);
}
//使用
template <typename   T1,   typename   T2,   typename   R>
R   mul_them(T1   v,T2   u)   {
returnv*u;
}
Caculate_Container(ai1,   ai2,   ai3,   mul_them <int,   int,   int> );
Caculate_Container(af1,   ad2,   ad3,   mul_them <float,   double,   double> );
如果容器元素有所变化,C#代码必须重写算法CaculThem()。但C++不需要,由于mul_them <> ()本身是个函数模板,那么只需将这个函数模板用新的类型实例化一下即可。
C++的代码相对简单些,灵活性也更高些。但这还不是全部,C++还有一个最终极的解法,不需要循环,不需要创建模板算法,不需要写操作函数:
transform(c1.begin(),   c1.end(),   c2.begin(),   c3.begin(),   _1*_2);


[解决办法]
不过,我还是很欣赏楼主所写的,像是做学问的样子,也正因为这样,才有可能真正走到计算机编程的深处,发掘自身的潜力,让语言不囿于语言本身的限制,做自己想做的事。
C++本就是一门全新的语言,不能用C的眼光去看去学习,但对template的运用与编写着实是两回事,一般应用者尽量少用template编程,在做template之前必须要有很深层次的考虑与规划,只有在保证确实通用不存在歧异的基础上,才能进行相应的设计开发。
[解决办法]
在我看来,C++的亮点有这么几个:
一、泛型能力,包括操作符重载在内
二、值语意,包括析构函数在内
三、底层相关
四、它很刺激

它最大的问题在于语言本身的复杂性,它的复杂性是总总权衡的结果,可惜这些权衡并不总是明智的。有时候它只付出了复杂性,而没有什么回报。而且在它进化的每一步,它都只能使自己更加复杂。

Boost.lambda,在我看来是一种无奈。C++用户都很虔诚,如果他们的语言中缺少某些东西,那么他们自己来实现,而不是投向另一门语言。Boost.lambda十分美好,但是它背后隐藏了太多的痛苦。其所使用的高端技巧和实现上的复杂性,在普通用户看来,甚至是不可理解的。我大致可以理解它的原理,然而我一直不敢去窥探它的实现。C++领域似乎总有一些人高高在上,当你觉得自己正在成长为一个语言大师(我想这个称号只存在于C++中)的时候,却发现自己其实连一个学徒都算不上。


[解决办法]
Boost.lambda 完全是走错了方向,这个特性应该由语言本身提供,而不是提供一个库来笨拙地模拟。
而且这种模拟代价高昂,就是极大地增加了编译时间,对编译器标准兼容性的要求也更高。

换个角度看,如果 C++ 标准委员会肯接受一个新关键字 delegate 和函数字面值,那么生活会美好得多

比如一个简单的 find 算法:
template <Iter, CallableType>
Iter find(Iter first, Iter last, CallableType pred) {
while(first != last) {
if(pred(first)) return first;
++first;
}
return last;
}

使用函数字面值:
ex1:
find(vec.begin(), vec.end(), (int x) { return x == 123; });
ex2:
void delegate(int) pred = (x) { return x == 123; }; //省略声明 x 类型,因为编译器可以推导
find(vec.begin(), vec.end(), pred);

[解决办法]
不要指望一种开发语言能给你包办一切,你要什么,他就给你什么.
好多问题也不是你想的那么简单,你认为那个功能很重要,语言就要给你提供那个功能,首先,大师比我们的眼光要深远多少,多我们思考问题缜密,所以不要只以自己个人的经历和需求来评判语言的好坏,C++是给所有人服务的,他所要满足的是大多数的程序员的功能需求.
C++与其它语言最大的区别在于C++是一种艺术,而其它语言很大程度上只能算得上是一种工具.
艺术能能你无限的创造力,而工具,能给你的生活带来方便时,你最丧失了最根本的创造力.
写程序就是一个不断创造的过程,过程是痛苦的,但结果总是美好的.
如果一直都利用别人提供的类库,而不试图自己去面临一些困境,并将其解决,开发出软件中属于自己的核心价值,那么中国的软件可能将一直处在外包的市场中.
什么时候,能有一个软件让世界认识中国!!

热点排行