C++真他妈复杂

2019年4月21日

基本类型

先讲C++基本类型吧。第一个问题就是,字符串不是基本类型!!字符串通常用两种类型表示,std::stringchar*,后者才是最原始的字符串类型,它是C时代的产物,有人说在C++里用std::string就好。那main函数的第二个参数还是必须为char*[],而且字符串字面值”hello”的类型是char*,经常不能直接传给string。初学者没有经验,如果没有系统学习,盲目搜索“C++ 字符串”,可能搜到字符串的各种表示,如wchar_t*、wstring、TCHAR、LPSTR、OLECHAR、_bstr_t ……

C++的bool可以取值true或false,true、false都是关键字。但是int也可以直接赋值给bool,难道bool是int的派生类型?std::cout << true,结果为1,似乎bool和int可以相互转化,令人困惑。比较起来,Java需要强制类型转换才能把bool变成int,说明bool和int是两种独立类型。JavaScript console.log(true)输出的是true,说明bool是一种原始类型。

string.find()返回类型是size_t,但可以赋值给int,那为什么不直接是int(原因省略),不直观。Java、C#就是返回int或long,非常一致。

数组

C++的数组,既可以看成数组,又可以看成指针,这种“波粒二象性”令人困扰。

复杂类型

C++可以对一个类型取引用、取指针。比如int指针类型定义为int*p=&a,这里把变量a取地址,赋给指针p。int引用定义为int&p=a。同一个&符号,在左边在右边意思不一样!而且,*前后有没有空格都没有关系。我个人觉得int* p表示p是int*类型,然而大部分人似乎写成int *p,看起来*就像是对p的运算符。

C++可以声明变量或参数为const,而且经常不得不用const。比如char* c_arguments[] = { "hi","bye" }就会出错,const char* c_arguments[] = { "hi","bye" }才可以。在Java、C#里,final、const基本上是加强自我约束的东西,编译器不会逼着你写。

取引用、取指针、数组、const,合起来可以写非常复杂的复杂类型,比如char*(*arr[10])(int*&p);,读作arr是一个有10个成员的数组,成员类型为指针,指针指向一个函数,该函数的参数的名称为p,类型为int的指针的引用,函数返回值为char引用。复杂类型的读法是左右散开的方式。类似的东西,用C#来写就是func<int,char[]>[] arr,简洁易懂。类型在左边,变量名在右边。不需要也不允许指定那个函数参数的名称。

函数调用

C++要求函数定义必须出现在函数调用之前。如果这个规定是强制的,也就算了,因为Bash、JavaScript都是这种要求。好死不死,C++还有个函数声明的概念,其实只要函数声明在函数调用之前就可以了,因为这个,就出现了所谓头文件(.h)的东西,头文件里只写声明,cpp文件里写定义。

函数也可以有指针,这样操作就很烦了。

面向对象

有两种访问成员运算符,.->。如果变量是指针,就要用->。实际上,也可以用(*pt).member。这样凭空多出来一个->运算符。

要学C++,建议读《Ivor Horton's Beginning Visual C++ 2008》。这本书系统性介绍C++,语言幽默。它也顺便介绍C++/CLI,类似用C++语法写托管代码。C++/CLI如今看来是过时了。