第 3 章:字符串、向量和数组

  1. 头文件中不应该包含 using 声明,不然每一个包含了改头文件的文件都会有这个声明,导致不可预料的命名冲突。

  2. 每个 string 对象的末尾都有一个 '\0',名为“空字符”,用于指示字符串的结束。如果在程序中人为的修改了这个空字符,那么会发生不可预料的结果。

  3. string 类型的初始化中,如果用到了赋值运算符 =,那么就是拷贝初始化,右值会先被拷贝一份,然后把拷贝后的内存交给新创建的对象。其余的都是直接初始化,分配一块内存然后直接填值。

  4. 在从输入流读取 string 的时候,cin1.1. 会忽略掉开头的所有空白 (包括空格、'\n''\t' 等),并从第一个合法的字符读起,直到遇到了下一处空白为止 (这处空白不会存在 string 中)。例如,如果输入的是 " houston ",那么这个 string 最终的内容是 houston。如果输入的是 " h o u s t o n ",那么这个 string 只能拿到 "h"

  5. 如果不想上面那样子的事情发生,可以使用 getline(in, str) 函数解决。该函数可以读入一整行,其中的两个参数 in 代表一个输入流,一般用 cinstr 代表要读入的字符串的标识符。所以要在调用 getline(in, str) 函数之前新建一个空字符串。注意,当你在一开始就按下了回车键,那么 getline 拿到的就是一个空串。正常输入的话,空白都会被保留,直到你按下换行键。换行键会被流读取到,但是不会存到 str 中。所以如果输入的是 " h o u s t o n ",然后按下换行符,那么这个 string 拿到的就是 " h o u s t o n "

  6. string::size() 返回的是 string::size_type 类型的数字,这是一个无符号类型 unsigned long 的数字,所以注意:当你要用到某个 stringsize() 函数的时候,那么其他介入的数字最好也都是无符号类型的,这样不会发生意想不到的错误。否则,如果你拿 str.size()-1 相比,那么既有可能返回的是 -1 更大,因为 -1 会先转成一个无符号数字,结果可是 2^64-1 啊!

  7. 当使用字符串的加法时,要确保每个加号两边都至少有一个对象是 string 类型。两个字符串字面值由于是 const char [] 类型,所以它俩是不能直接相加的,例如 "hello" + ", world" 是不合法的。

  8. range for 语句:

    for (auto declaration : expression)
    { statement }
    

    其中的 auto 可以自动探测变量类型,expression 部分是要被遍历的序列,declaration 是一个变量,它代表序列中每次被遍历到的单个元素。举个例子:

    string str = "hungry!"
    for (auto c : str)
    { cout << c << '-'; }
    // "h-u-n-g-r-y-!-"
    
  9. 字符串的下标 (索引) 会自动转为 string::size_type 这个无符号的 unsigned long。所以,如果你访问 str[-1],其实你在 x64 的机器上得到的是 str[18446744073709551615]。在访问字符串的某一位的时候,一定要确保该位确实有值。如果这个字符串本身为空,那么你的访问得到的结果是未定义的。所以在访问数组下标的时候,推荐的做法是:总是使用 decltype(str.size())这个类型来声明所有下标的类型。

  10. vector 是模版而非类型。给 vector 提供了具体的元素类型之后,他才是一个类型,例如 vector<string>

  11. vector 能容纳绝大多数类型的对象,包括内置类型、用户自定义类型和其他容器。但是不能够容纳引用,因为引用只是一个别名而不是一个对象。当使用一些老旧的编译器的时候,声明一个 vectorvector 元素的时候,要在第一个右尖括号和第二个右尖括号之间插入一个空格,例如 vector<vector<string> >,这一点要稍微注意一下。不过现在使用的编译器版本都是不需要加这个空格的啦!

  12. range for 语句内不能改变其所遍历序列的大小。但凡是使用了迭代器的循环器,都不要想迭代器所属的容器添加元素。

  13. 只能对确知已存在的元素执行下标操作。否则会导致缓冲区溢出。

  14. 如果容器为空,那么 beginend 返回的是同一个迭代器,都是尾后迭代器。

  15. 执行解引用的迭代器必须合法并确实指示着某个元素。试图解引用一个非法迭代器或者尾后迭代器都是未被定义的行为。

  16. 两个迭代器相减得到的是一个 difference_type 的带符号类型。

  17. 在声明数组的时候,要么使用常量表达式说明数组的长度,要么使用字面值。不能使用变量来声明数组的大小。定义数组的时候必须指定数组类型,不允许使用 auto,哪怕你提供了初始值列表也不行。和 vector 一样,不存在引用的数组,但是有指针的数组。可以定义数组的指针和数组的引用:

     constexpr int sz = 10;    // 常量表达式
     int arr[sz];              // 常量表达式初始化数组
     int *ptrs[sz];            // 指针的数组
     int (*Parr)[sz] = &arr;   // Parr 指向 arr
     int (&Rarr)[sz] = arr;    // Rarr 引用 arr
     int *(&Rptrs)[sz] = ptrs; // Rptrs 引用 ptrs
    

    理解上述代码可能有些困难,有一个简单的方法就是看有没有括号。括号的作用是强行绑定几个元素,所以当 * 或者 & 出现在括号里头的时候,它们归属于括号里它们右边的标识符;当 *& 没有被括号限制的时候,它们归属于左边的类型。例如 ptrs,它左边的 * 没有被括号所限制,那么 * 和左边的 int 在一起,指示的是数组的元素为指针类型。再看 Parr* 被括号强行限制住了,那么 * 归属于 Parr,代表 Parr 自己是个指针。同样的,Rarr 左边的 &Rarr 绑定,那么代表 Rarr 本身是一个引用。最后一个 Rptrs 也就好理解了,& 说明 Rptrs 是个引用,* 说明数组的元素都为指针类型。

  18. 前一章讲到过 autodecltype 的区别:auto 不能保留引用类型和顶层常量属性,但是 decltype 可以。现在指出新的一点:由于单独操作数组名字时其实操作的是一个指针,所以当把数组名赋值给另一个变量的时候,这个变量用 auto 检测到的类型是指针而不是数组:

     int a1[10] = {0,1,2,3,4,5,6,7,8,9};
     auto a2 = a1;  // a2 是一个 int * 指针,指向 a1 的第一个元素
     for (auto i : a2) {}; // 编译错误, a2 不是一个数组,所以不能用 auto
     decltype(a1) a3 = {1,2,3,4,5,6,7,8,9,10}; // a3 是一个 int[10] 数组
     a3 = &(a1[2]); // 编译错误,a3 是一个数组,不能给它赋地址
    

    但是 decltype 却不是这样,检测到的类型并不是指针而同样是一个数组。由此可见,decltype 非常的底层,它能够突破引用和数组的表象深挖到对象的本质,即引用类型和数组类型;而 auto 会把引用当成一个别名,会把数组当成一个指针。

  19. <iterator> 头文件中有两个函数 begin()end(),能够接受数组作为参数,返回数组的头指针和尾后指针,就像迭代器那样。

  20. 两个数组指针相减得到的是一个 ptrdiff_t 的带符号类型。

  21. 标准库类型 stringvector 可以执行下标运算,但是限定下标是无符号类型。然而数组的下标运算符 [] 可以接受有符号的下标,例如 p[-2] 代表向前移动 p 两个单位。下标运算符的本质是对指针进行位移。

  22. <string> 是标准库头文件,<string.h> 是 C 风格字符串的头文件,<cstring><string.h> 的 C++ 风格。写 C++ 程序时,应尽量使用标准库 <string>

  23. <cstring> 提供一些 C 风格字符串的函数,例如 strlen(p)strcmp(p1, p2)strcat(p1, p2)strcpy(p1, p2)等。传入此类函数的指针必须指向一个明确以空字符作为结束的 char 数组,否则会发生严重错误。

  24. 为了方便老旧代码和新标准代码的混用,C++ 允许使用字符串字面值来初始化 string 对象,允许使用以空字符结束的字符数组 (又称 C 风格字符串) 来初始化 string 对象或赋值,允许将 string 对象与 C 风格字符串相加。这是新特性向下兼容旧特性。所以反过来是不成立的,不能用 string 对象来直接初始化一个指向字符的指针。为了完成这一功能,string 专门提供了一个函数 c_str(),能够接受一个 string 对象,传出一个指针,指向内容一模一样的只读的 C 风格字符串:

    string s("C++ is hard");
    char *cs = s; // 错误!不能用 string 对象初始化 char *
    const char *str = s.c_str(); // OK
    
  25. 可以使用 begin()end() 来用数组初始化一个 vector:

    int ar[] = {2,3,5,7,11,13,17};
    vector<int> v(begin(ar), end(ar));
    

    在上述代码中,两个指针指明了用来初始化的值的区间,所以你可以任意指定合法的区间来初始化。注意,第二个参数应是带拷贝区域的尾后指针,也就是说如果你要初始化数组的第 0 位到第 4 位,你需要传入的是

    vector<int> vv(begin(ar), begin(ar)+5);
    
  26. 在使用范围 for 语句处理多维数组的时候,要把索引写成引用,这样子可以避免索引被转为指针:

    int arr[2][3] = { {1,3,5}, {2,4,6} };
    for (auto &r : arr) {
        for (auto &c : r) {
           c *= 3;
        }
    }
    

    如果不把 r 声明称引用,那么 r 会被处理成指针,内层的 for 语句就不能正确编译了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容