前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《C++Primer》第三章 字符串、向量和数组

《C++Primer》第三章 字符串、向量和数组

作者头像
TOMOCAT
发布2020-10-28 11:35:51
5290
发布2020-10-28 11:35:51
举报
文章被收录于专栏:懂点编程的数据分析师

写这篇文章的目的

身为C++的零基础初学者,短期内把《C++Primer》啃下来是一个比较笨但是有效的方法,一方面可以掌握比较规范的C++语法(避免被项目中乱七八糟的风格带跑偏),另一方面又可以全面地了解C++语法以及C++11新标准(后续要做的事情就剩下查漏补缺,不断完善自己的知识体系)。

个人感觉从零学习一门新知识比较好的方法是快速了解知识的全貌,然后构建自己的知识地图,后续不断地补充相应的细节。

由于《C++Primer》和大多数的教科书一样废话连篇,因此想要精炼一下每篇文章的内容再打印成pdf,方便温故知新。

全文链接

命名空间的using声明

  • using namespace::name;指的是编译器应从操作符左侧名字所示的作用域寻找右侧的名字
  • 头文件中不应该出现using声明:这是因为头文件中的内容会拷贝到所有引用它的文件中去,这会导致每个使用了该头文件的文件就会有这个声明,这并非我们所预期的

标准库类型string

代码语言:javascript
复制
#include <string>
using std::string;
1. 定义和初始化string对象
  • 拷贝初始化copy initialization:使用等号=初始化一个变量,编译器把等号右侧的初始值拷贝到新创建的对象中
  • 直接初始化direct initialization:不使用等号
代码语言:javascript
复制
string s1 = "hiya";   // 拷贝初始化
string s2("hiya");    // 直接初始化
string s3(10, 'c');   // 直接初始化, s7内容为cccccccccc

// 拷贝初始化
string s4 = string(10, 'c');
// 等价于
string temp = string(10, 'c');
string s4 = temp;
2. string对象上的操作
代码语言:javascript
复制
os<<s                  将s写到输出流os中,返回os
is>>s                  从is中读取字符串赋给s,字符串以空白分隔,返回is
getline(is, s)         从is中读取一行赋给s,返回is
s.empty()              s为空返回true
s.size()               返回s中字符个数
s[n]                   返回s中第n个字符的[引用]
s1 + s2                返回s1和s2连接后的结果
s1 = s2                用s2的副本代替s1中原先的字符
s1 == s2               大小写敏感
s1 != s2
<, <=, >, >=           利用字符在字典中的顺序比较, 且对大小写敏感
  • 读写string对象:cin读取时会忽略开头和结尾处的空白;getline()函数从给定的输入流中读入内容直到遇到换行符(注意换行符也被读进去了)
  • string::size_type类型:size()函数返回的是一个string::size_type的类型,它本身是一个无符号正数,切记表达式中已经有了size()函数就不要再使用int了,否则可能会出现意想不到的错误:比如s.size() > nn为负数时判断结果几乎肯定是true
3. 访问string元素

遍历获取string元素:

代码语言:javascript
复制
string str("some string");
for (auto c : str)
   cout << c << endl;

前面讲到当引用被用作初始值时,真正参与初始化的其实是引用对象的值,此时编译器以引用对象的类型作为auto类型。

因此如果我们需要获得字符的引用来改变string,需要把循环变量定义为引用类型:

代码语言:javascript
复制
string s("Hello World!!!");
// 转化为大写
for (auto &c : s)
    c = toupper(c);
cout << s << endl;

标准库类型vector

代码语言:javascript
复制
#include <vector>
using std::vector;
1. 定义及初始化
代码语言:javascript
复制
vector<T> v1;            // 空vector, 执行默认初始化
vector<T> v2(v1);        // v2包含v1所有元素的副本
vector<T> v2 = v1;       // 同上
vector<T> v3(n, val);    // v3包含n个重复的元素, 每个元素的值都为val
vector<T> v4(n);         // v4包含n个执行了值初始化的对象
vector<T> v5{a,b,c...};  // v5包含了初始值为列表中的元素
vector<T> v5={a,b,c...}; // 同上
2. 添加元素及其他操作

C++标准要求vector应该能在运行时高效快速地添加元素,因此在定义vector对象时设定其大小也就没有必要了,事实上这么做可能性能更差。只有一种例外情况:即所有元素的值都一样。一旦元素的值有所不同,更有效的方式是先定义一个空的vector对象,再在运行时向其添加具体值。

大部分的操作都和string类似:

代码语言:javascript
复制
v.empty()          // 是否为空
v.size()
v.push_back(t)     // 尾部插入
v[n]
v1 = v2
v1 = {a,b,c...}    // 用列表中的元素拷贝替换v1中的元素
v1 == v2           // 当且仅当元素数量相同且相应位置的元素值都相同
v1 != v2
<, <=, >, >=       // 以字典顺序比较

注意:

  • 如果想遍历并修改vector中的值,可以在循环条件写for (auto &i : v)
  • vector<T>的下标类型为vector<T>::size_type
  • vectorstring对象的下标运算符可用于访问已存在的元素而不能用于添加元素,试图用下标访问一个不存在的元素将引发错误

迭代器iterator

代码语言:javascript
复制
// 由编译器决定b和e的类型, b表示v的第一个元素, e表示v尾元素的下一个元素
// 注意当容器为空时, begin和end都返回同一个迭代器, 都是尾后迭代器
auto b = v.begin(), e = v.end(); // b和e类型相同
代码语言:javascript
复制
*iter       // 返回迭代器iter所指元素的引用
iter->mem   // 解引用iter并获取该元素名为mem的成员, 等价于(*item).mem
++iter      // 指向容器中下一个元素
--iter      // 指向上一个元素
iter1 == iter2  // 如果两个迭代器是同一个元素或者都是同一个容器的尾后迭代器, 则相等
iter1 != iter2

使用迭代器遍历string

代码语言:javascript
复制
for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it)
    *it = toupper(*it); // 将当前字符改成大写

迭代器类型:

C++11中通过cbegin()cend()函数可以得到const_iterator迭代器。

代码语言:javascript
复制
vector<int>::iterator it;    // it能读写vector<int>元素
string::iterator it2;        // it能读写string中字符

vector<int>::const_iterator it3;  // it3只能读元素而不能写元素
string::const_iterator it4;       // it4只能读字符而不能写字符

数组

vector不同的是,数组的大小确定不变,不能随意向数组中添加元素。这种特性使得对于某种特殊的应用而言程序的运行时性能更好,但是也损失了一些灵活性。

1. 定义和初始化内置数组

和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组内含有未定义的值。

代码语言:javascript
复制
// 显式初始化数组元素
int a1[3] = {0, 1, 2};
int a2[] = {0, 1, 2};   // 维度为3
int a3[5] = {0, 1, 2};  // 等价于a3[] = {0, 1, 2, 0, 0};

// 字符数组的特殊性
char a1[] = {'C', '+', '+'};         // 列表初始化, 不带空字符
char a2[] = {'C', '+', '+', '\0'};   // 列表初始化, 含有空字符
char a3[] = "C++";                   // 自动在末尾添加空字符
  • 不允许用一个数组初始化另一个数组,也不允许用一个数组给另一个数组赋值
  • 复杂的数组声明
代码语言:javascript
复制
int *ptrs[10];             // 含有10个整型指针的数组
int (*Parray)[10] = &arr;  // Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr;   // arrRef引用一个含有10个整数的数组
2. 访问数组元素

vectorstring一样,当需要遍历数组的所有元素时,最好的方法是使用范围for语句:

代码语言:javascript
复制
for (auto i : scores)
    cout << i << " ";
cout << endl;
3. 指针和数组

C++中,指针和数组由非常紧密的联系,使用数组的时候编译器一般会把它替换为一个指向数组首元素的指针。

C++11新标准引入了两个名为beginend的函数:

代码语言:javascript
复制
int *pbeg = begin(arr), *pend = end(arr);
// 寻找第一个负值元素
while (pbeg != pend && *pbeg >= 0)
    ++pebg;

只要指针指向的是数组中的元素,都可以执行下标操作:

代码语言:javascript
复制
int *p = &ia[2];    // p指向索引为2的元素
int j = p[1];       // p[1]等价于*(p + 1), 即ia[3]
int k = p[-2];      // p[-2]即ia[0]
4. C风格字符串

虽然C++支持C风格字符串,但最好还是不要使用。这是因为不仅使用起来不太方便,而且极易引发程序漏洞,是诸多安全问题的根本原因。

一些可操作C风格字符串的函数定义在cstring头文件中,cstringC语言头文件string.hC++版本:

代码语言:javascript
复制
strlen(p)          // 返回p的长度, 空字符不算在内
strcmp(p1, p2)     // 比较p1和p2的相等性
strcat(p1, p2)     // 将p2附加到p1之后, 返回p1
strcpy(p1, p2)     // 将p2拷贝到p1, 返回p1

对于C++中的string,比较大小时是使用普通的关系运算符和相等性运算符;但是C风格字符串需要使用strcmp函数。

如果我们要实现两个C风格字符串拼接,正确的方法是使用strcatstrcpy函数,还需要使用一个用于存放结果字符串的数组,例如下面的代码虽然很常见,但是充满了安全风险,极易引发错误:

代码语言:javascript
复制
// 如果我们计算错了largeStr的大小将引发错误
strcpy(largeStr, cal);     // 将ca1拷贝到largeStr
strcat(largeStr, car2);    // 将ca2连接到largeStr后面

大多数情况下,使用标准库string要比C风格字符串更安全

与旧代码的接口

现代的C++程序中充满了数组或者C风格字符串的代码,因此C++专门提供了一组功能:

1. 混用string和C风格字符串
  • 允许使用以空字符结束的字符数组来初始化或赋值string对象
  • string对象的加法运算中允许使用以空字符结束的字符数组作为其中一个运算对象(不能两个对象都是)
  • 如果程序需要一个C风格字符串,可以通过c_str成员函数将string转化为C风格字符串
2. 使用数组初始化vector对象
代码语言:javascript
复制
int int_arr[] = {0, 1, 2, 3, 4, 5};
// ivec有6个元素,分别是int_arr中对应元素的副本
vector<int> ivec(begin(int_arr), end(int_arr));
// 只拷贝三个元素: int_arr[1], int_arr[2], int_arr[3]
vector<int> subVec(int_arr + 1, int_arr + 4);

多维数组

严格来说,C++中没有多维数组,通常所说的多维数组其实都是数组的数组。

初始化:

代码语言:javascript
复制
int ia[3][4] = { // 三个元素, 每个元素都是大小为4的数组
    {0, 1, 2, 3},
    {4, 5, 6, 7},
    {8, 9, 10, 11}
}

// 显式地初始化每行的首元素
int ia[3][4] = {{ 0 }, { 4 }, { 8 }};

// 显式地初始化第一行, 其他元素执行值初始化
int ix[3][4] = {0, 3, 6, 9}

遍历元素:

代码语言:javascript
复制
int ia[rowCnt][colCnt];
for (size_t i = 0; i != rowCnt; ++i) {
    for (size_t j = 0; i != colCnt; ++i) {
        ia[i][j] = i * colCnt + j;
    }
}

// 使用范围for语句
size_t cnt = 0;
for (autp &row : ia)        // 对于外层数组每一个元素
    for (auto &col : row) { // 对于内层数组每一个元素
        col = cnt;
        ++cnt;
    }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写这篇文章的目的
  • 全文链接
  • 命名空间的using声明
  • 标准库类型string
    • 1. 定义和初始化string对象
      • 2. string对象上的操作
        • 3. 访问string元素
        • 标准库类型vector
          • 1. 定义及初始化
            • 2. 添加元素及其他操作
            • 迭代器iterator
            • 数组
              • 1. 定义和初始化内置数组
                • 2. 访问数组元素
                  • 3. 指针和数组
                    • 4. C风格字符串
                    • 与旧代码的接口
                      • 1. 混用string和C风格字符串
                        • 2. 使用数组初始化vector对象
                        • 多维数组
                        相关产品与服务
                        容器服务
                        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档