现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

C++学习总结

2012-07-12 10:27 工业·编程 ⁄ 共 3912字 ⁄ 字号 暂无评论

    学习C++有半个多月了,感触比较多。以前一直是和虚拟机类语言(C#/Java)打交道的,尽管早已对C/C++的恶劣环境有所准备,但当开始学习一段时间以后还是不禁吃了一惊。

      本人阅读的是《C++ Primer》,这本书是C++标准委员会许多成员共同著作而成,权威性自然不需多说。书中频繁出现陷阱,注释的小Tip。注释一般是补充解释,提出某些建议或者方案。陷阱的Tip的频繁出现令人惊奇,这大概也是C++被人称作古怪语言的原因。

  废话不多说,上点干货吧:

1.让我想起了Python:

a.字符串分为宽字符串型和普通字符串型。

b.允许在语句中加入\来换行分割显示,编译时会将\忽略掉。

2.让我想起了C:

a.#define #if #ifndef #endif这组预处理。 C用它来做DEBUG开关,以及保护.h头。

b.内置类型的长度是与机器相关的。为了保证兼容性,C/C++一般提倡用.h中用typedef声明了一堆的别名来操作。(例如size_t云云的)。

c.整个世界是全局的,源文件之间通过extern来交流。

d.坚决的将变量分为const和非const两种。const相当于将内存上锁,只许访问不许写入。

e.数组变量在通常情况下解释为int指针变量。然而,更恰当的说法(私以为)是数组变量是一个const指针,它不能改变指向。

int a[2]; 
a=new int[2](); 
这个将产生编译错误。正确的写法应该是:
[cpp] view plaincopy
int a[2]; 
int *p=a; 
p=new int[3](); 
f.指针横行。与malloc,alloc,free相对应的。C++给出了delete,new,delete []。操作指针目标对象亦可使用->的便捷方式。

3.让我想起了Java:

a.#include<>:可不就是类似Java的BootstrapClassLoader,直接从jdk的指定lib文件/目录中查找么?

#include "":就是类似Java的AppClassLoader了,从App路径开始找。

b.vector可是Java中大名鼎鼎的Collections之一。C++标准库中的vector似乎与同步性是无关的。接触了C++模板后似乎开始懂得Java泛型中rawType的意思了。

遗憾的是,C++没有C#/Java中的foreach语法。为了操纵Iterator,不得不写一大堆的域操作符。糟糕的是,域操作符不可避免,因为C++模板并不是一个类。

c.对于比int小的整型来说,在计算前会自动提升为int来进行计算。C++中也是如此。

4.让我想起了C#:

a.用using来声明域。using namespace xxx可以在类内直接访问域成员,不需要域操作符::来直接访问。Java/C#/Python访问域均是直接'.'来获得,不知道C++为何如此另类。

b.使用引用。引用便是别名,C#中用ref关键字来指定,C++则用&来指定。
c.const,const <class>*与const *x:在C++中将与指针无关的const理解为#define,将与指针相关的const理解为C#的readonly。

d.操作符重载:也许这真的是个好东西,但是陷阱很多,而且对于新手也并不友好。

e.代理:C++版的delegate便是指针。

5.让我吓一跳的东西

a.C++模板不但可以传入类型,竟然也可以传入具体的值。bitset便是一个例子。

bitset<32> bitvec; 
32可不是类型。可千万别把C++模板当做基类。bitset<32>是一个类型,bitset<24>又是一个类型。

b.C++中的对象既可以在栈区创建,也可以在堆区创建。

Person p; 
这行语句便是调用默认构造器在栈区创建了一个Person对象。如果想要在堆区动态创建一个Person,则需要这么写:

Person *p=new Person(); 

c.C++中的数组和指针被称为复合类型。因此声名可是非常讲究的,《C++Primer》中建议对于指针变量从右向左进行阅读。

int *p[4]; 
int (*p2)[4]; 
第一个声名的是一个指针数组。(它是数组)

第二个声名的是一个指向数组的指针。(它是指针)

d.C++中的类分为声明和说明。声明一般放在.h文件中,只负责声明。实现体放在.cc文件中,负责对类定义进行实现。

C#/Java中的类定义是这样子的:

class A 

  public void method(){ 
//do something 

而C++的类定义却是这样子的:

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

//do something 

e.C++的初始化是非常烦人的东西。它区分初始化情况为内置类型初始化,以及非内置类型初始化。内置变量只有全局变量才会自动初始化,其它情况一概是随机值。为了保证内置变量的正确初始化,必须手动进行初始化赋值或者在新创建时显示调用()来初始化。

C++中的构造器也非常的古怪。C++不像C#/Java一样,初始化的流程并不允许直接的字段初始化。C++只允许在构造器中做声明初始化。

f.C++中的抛异常很非主流。对于Java/C#来说,一般都是抛出一个继承自Exception基类的异常实例。而C++抛异常却是调用函数。

throw run_time_error("abcdefg"); 

g.C++中有一种称为默认实参的功能。这种功能很强大,但是可能会引起重载混乱。总体来说,这是一个好功能。

int test(int a=12); 

h.C++中函数体中的静态变量。

void test() 

static size_t called=0; 
called++; 

其中的静态变量called在首次调用时会初始化。之后的调用会绕开定义,直接访问变量。本人猜测这是预编译处理的一种语法特性。

6.让我一直警惕的C++特性:

a.预编译:

C/C++相比较虚拟机高级语言来说,多了一个预编译环节。这个环节非常重要,很容易漏掉预编译这部分的思考。区分清楚预编译,能够很大程度上掌握C/C++语法上一些古怪特性的背后原因。比如sizeof,它就是一个预编译阶段的东西。

b.cstring与string:

std内置的String类型与字面量不是一回事情,所有的字面量都是CString。按道理来说,

bool isEmpty(const String& s) 

  return s.size()==0; 

 
isEmpty("abc") 

类似这样的代码应该是compile不过去的。因为函数要求传入的是String类型的数据,而实参却是CString。令人奇怪的却是编译没有问题,运行也正常。C++一定是在编译期间背地里做了点手脚。

c.危险的数组:

C#/Java等高级语言,一般会将数组包装成为一个Array对象,其中会包含数组长度的信息。当数组越界时也会迅速报告。而C/C++中,数组就是数组。程序员需要自己维护数组,包括维护数组长度的变量(静态数组)。需要显示的delete [] xx来释放数组资源。

d.危险的类型转换:

C#/Java中类型转换都是安全的,对于C#来说,甚至可以指定implict和explict来添加语法。

C++的内置类型的自动转型是很危险的。数值类型运算前会根据两者类型来做“提升”。糟糕的是,若两者的size一样大,而一个有符号,一个无符号或者一个是浮点数,一个是整数时,转换的预期结果将很不确定。

而对于显示转换来说,C++提供了dynamic_cast,static_cast,const_cast,reinterpret_cast。这些转换很奇怪,reinterpret_cast重新解析了底层位模式结构。也就是说,它只更改类型,但不更改类型的真实内容,仅仅在指针cast中有用,平常是相当危险的东西。const_cast可以将只读的const变量cast成可读写的,非常奇怪的语法。

最后,从一个Java开发人员的角度来说。关于bool类型的自动转换让人感觉不可靠,也很难懂。

e.危险的enum
C++enum的声明与C#没有什么两样。但是事实上,每个enum都对应一个常量类型。并不是所有enum都是int或者long的。enum中的常量数值会根据编译器分析,最终决定一个刚好适配大小的实际类型。

enum Tokens {INLINE=128,VIRTUAL=129} 

其中INLINE可能是unsigned char型,VIRTUAL是int型。

f.麻烦的函数重载:

函数重载会经过候选函数->可行函数->最佳匹配这几个步骤。当加入了默认实参时,函数重载变得晦涩难懂。

总结:

有人说《C++ Primer》过于复杂,过于细节,只适合做字典来使用。真正的C++入门教材应该是《Accelerate C++》这样子轻松简单的。

的确,《C++ Primer》不适合初学程序的人阅读。只有对程序设计有了足够深入了解后,才能有所体会。

C++这门语言语法庞杂,陷阱很多。Linus不屑一顾的称C++是门不合格的语言,CSDN的头版也曾有C++是2012年不宜进入的几门技术之一。《JavaScript语言精粹》的作者建议说:每一门语言都有其鸡肋、糟粕、精华的部分。了解全部知识,能够去除鸡肋、糟粕,发扬精华部分,才能成为这门语言的掌握者。

C++继承了C语言的预处理、指针、标准库。添加了模板、类等高层次的封装。重用方面和减少资源消耗方面做了很多努力,这就使得C++变得复杂怪异起来。

给我留言

留言无头像?