C++学习总结

来源:互联网 发布:大数据解决方案译见 编辑:程序博客网 时间:2024/06/12 01:46

  学习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]();
这个将产生编译错误。正确的写法应该是:
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++变得复杂怪异起来。