C++ Primer 第3章 字符串、向量和数组

news/2024/5/19 5:27:32/文章来源:https://blog.csdn.net/weixin_42589774/article/details/132594944

C++ Primer 第3章 字符串、向量和数组

  • 3.1 命名空间的using声明
    • 一、每个名字都需要独立的using声明
    • 二、头文件不应包含using声明
    • 三、一点注意事项
  • 3.2 标准库类型string
    • 3.2.1 定义和初始化string对象
      • 一、直接初始化和拷贝初始化
    • 3.2.2 string对象上的操作
      • 一、读写string对象
      • 二、读取未知数量的string对象
      • 三、使用getline读取一整行
      • 四、string的empty和size操作
      • 五、string::size_type类型
      • 六、比较string对象
      • 七、为string对象赋值
      • 八、两个string对象相加
      • 九、字面值和string对象相加
      • 十、练习
    • 3.2.3 处理string对象中的字符
      • 一、处理每个字符使用基于范围的for语句
      • 二、使用范围for语句改变字符串中的字符
      • 三、只处理一部分字符
      • 四、使用下标执行迭代
      • 五、使用下标执行随机访问
  • 3.3 标准库vector
    • 3.3.1 定义和初始化vector对象
      • 一、列表初始化vector对象
      • 二、创建指定数量的元素
      • 三、值初始化
      • 四、列表初始值还是元素数量?
    • 3.3.2 向vector对象中添加元素
      • 一、向vector对象添加元素蕴含的编程假定
    • 3.3.3 其他vector操作
      • 一、计算vector内对象的索引
      • 二、不能用下标形式添加元素
  • 3.4 迭代器介绍
    • 3.4.1 使用迭代器
      • 一、迭代器运算符
      • 二、将迭代器从一个元素移动到另外一个元素
      • 三、迭代器类型
      • 四、begin和end运算符
      • 五、结合解引用和成员访问操作
      • 六、某些对vector对象的操作会使迭代器失效
      • 七、练习
    • 3.4.2 迭代器运算
      • 一、迭代器的算术运算
      • 二、使用迭代器运算
  • 3.5 数组
    • 3.5.1 定义和初始化内置数组
      • 一、显式初始化数组元素
      • 二、字符数组的特殊性
      • 三、不允许拷贝和赋值
      • 四、理解复杂的数组声明
    • 3.5.2 访问数组元素
      • 一、检查下标的值
    • 3.5.3 指针和数组
      • 一、指针也是迭代器
      • 二、标准库函数begin和end
      • 三、指针运算
      • 四、解引用和指针运算的交互
      • 五、下标和指针
    • 3.5.4 C风格字符串
      • 一、C标准库String函数
      • 二、比较字符串
      • 三、目标字符串的大小由调用者指定
    • 3.5.5 与旧代码的接口
      • 一、混用string对象和C风格字符串
      • 二、使用数组初始化vector对象
  • 3.6 多维数组
      • 一、多维数组的初始化
      • 二、多维数组的下标引用
      • 三、使用范围for语句处理多维数组
      • 四、指针和多维数组
      • 五、类型别名简化多维数组的指针
      • 六、练习
  • 小结

3.1 命名空间的using声明

using naspace::name;
using std::cin;
using std::cout;
using std::endl;

一、每个名字都需要独立的using声明

每个using声明引入命名空间中的一个成员。

二、头文件不应包含using声明

位于头文件的代码一般来说不应该使用using声明。这是因为头文件的内容会拷贝到所有引用它的文件中去,如果头文件里有某个using声明,那么每个使用了该头文件的文件都会有这个声明。对于某些程序来说,由于不经意间包含了一些名字,反而可能产生始料未及的名字冲突。

三、一点注意事项

3.2 标准库类型string

标准库类型string表示可变长的字符序列,使用string类型必须首先包含string头文件,string定义在命名空间std中。

# include <string>
using std::string;

3.2.1 定义和初始化string对象

==表3.1==

一、直接初始化和拷贝初始化

如果使用等号(=)初始化一个变量,实际上执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去。如果不使用等号,则执行的是直接初始化。
拷贝初始化:创建临时对象,复制构造函数。直接初始化效率更高

string s5 = "hiya"; // 拷贝初始化
string s6("hiya"); // 直接初始化
string s7(10, 'c'); // 直接初始化
string s8 = string(10, 'c'); // 拷贝初始化,需要显式地创建一个临时对象用于拷贝

3.2.2 string对象上的操作

在这里插入图片描述

一、读写string对象

#include <iostream>
#include <string>using std::cin;
using std::cout;
using std::endl;
using std::string;int main()
{string s1, s2;cin >> s1 >>s2;; // 在执行读取操作时,string对象会自动忽略开头的空白(空格符、换行符、制表符)并从第一个真正的字符开始读起,直到遇见下一处空白为止cout << s1 << s2 << endl;return 0;
}

二、读取未知数量的string对象

#include <iostream>
#include <string>using std::cin;
using std::cout;
using std::endl;
using std::string;int main()
{string word;while (cin >> word) {cout << word << endl;}return 0;
}

三、使用getline读取一整行

getline函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止(换行符也被读进来了),然后把所读的内容存入到那个string对象中去(注意不存换行符)。getline只要一遇到换行符就结束读取操作并返回结果,哪怕输入的一开始就是换行符也是如此。如果输入真的一开始就是换行符,那么所得到的结果是个空string。getling也会返回它的流参数。

#include <iostream>
#include <string>using std::cin;
using std::cout;
using std::endl;
using std::string;int main()
{string line;// getline不忽略开头和结尾的空白while (getline(cin, line)) {cout << line << endl;}return 0;
}

四、string的empty和size操作

empty函数根据string对象是否为空返回一个对应的布尔值。

string line;
while (getline(cin, line)) {if (!line.empty()) {cout << line << endl;}
}

size函数返回string对象的长度(即string对象中字符的个数)。

string line;
while (getline(cin, line)) {if (line.size() > 80) {cout << line << endl;}
}

五、string::size_type类型

size函数返回的是一个string::size_type类型的值。
string类及其他大多数标准库类型都定义了几种配套的类型。这些配套类型体现了标准库类型与机器无关的特性,类型size_type即使其中的一种。它是一个无符号类型的值,而且能足够存放下任何string对象的大小。
注意:由于size函数返回的是一个无符号整数类型,因此切记,不要在表达式中混用带符号数和无符号数。

六、比较string对象

相等性运算符(==和!=)分别检验两个string对象相等或不相等,string对象相等意味着他们的长度相同而且所包含的字符也全都相同。关系运算符<、<=、>、>=分别检验一个string对象是否小于、小于等于、大于、大于等于另外一个string对象。

七、为string对象赋值

string st1(10, 'c'), st2;
st1 = st2; // 赋值:用st2的副本替换st1的内容

八、两个string对象相加

string s1 = "hello, ", s2 = "world\n";
string s3 = s1 + s2; // s3的内容是hello, world\n
s1 += s2; // 等价于s1 = s1 + s2

九、字面值和string对象相加

标准库允许把字符字面值和字符串字面值转换成string对象。

string s1 = "hello", s2 = "world";
// 当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string
string s3 = s1 + ", " + s2 + '\n'; // 正确
string s4 = s1 + ", "; // 正确
string s5 = "hello" + ", "; // 错误,两个运算对象都不是string,双引号的字符串实际是地址,两个地址相加无法转换为string对象
string s6 = s1 + ", " + "world"; // 正确
string s7 = "hello" + ", " + s2; // 错误 
cout << *"Hello world" << endl; // H

十、练习

// 当cin函数输入错误的时候,cin里面有个函数可以自动检测到输入错误,若想继续输入便要清除掉这个错误
cin.clear();
// 将输入的错误字符清理掉
cin.sync();

3.2.3 处理string对象中的字符

==表3.3==
C++标准库中除了定义C++语言特有的功能外,也兼容了C语言的标准库。C语言的头文件形如name.h,C++则将这些文件命名为cname。特别的,在cname的头文件中定义的名字从属于命名空间std。

一、处理每个字符使用基于范围的for语句

范围for语句:遍历给定序列中的每个元素并对序列中的每个值执行某种操作。

expression部分是一个对象,用于表示一个序列
declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素
每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值
for (declaration : expression)statement
string str("some string");
for (auto c : str) {cout << c << endl;
}string s("Hello World!!!");
decltype(s.size()) punct_cnt = 0;
for (auto c : s) {if (ispunct(c)) {++punct_cnt;}
}
cout << punct_cnt << " punctuation characters in " << s << endl;

二、使用范围for语句改变字符串中的字符

string s("Hello World!!!");
for (auto &c : s) {c = toupper(c);
}
cout << s << endl; // HELLO WORLD!!!

三、只处理一部分字符

下标运算符([ ])接收的输入参数是string::size_type类型的值,这个参数表示要访问的字符的位置;返回值是该位置上字符的引用。

if (!s.empty())cout << s[0] << endl;string s("some string");
if (!s.empty())s[0] = toupper(s[0]);

四、使用下标执行迭代

逻辑与运算符(&&):如果参与运算的两个运算对象都为真,则逻辑与结果为真;否则结果为假。C++语言规定只有当左侧运算对象为真时才会检查右侧运算对象的情况。

五、使用下标执行随机访问

#include <iostream>
#include <string>using std::cin;
using std::cout;
using std::endl;
using std::string;int main()
{const string hexdigits = "0123456789ABCDEF";cout << "Enter a series of numbers between 0 and 15"<< " separated by spaces. Hit ENTER when finished: "<< endl;string result;string::size_type n;while (cin >> n) {if (n < hexdigits.size()) {result += hexdigits[n];}}cout << "Your hex number is: " << result << endl;return 0;
}

3.3 标准库vector

标准库类型vector表示对象的集合,其中所有对象的类型都相同。

#include <vector>
using std::vector

C++语言既有类模板,也有函数模板,其中vector是一个类模板。模板本身不是类或函数,可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为实例化,当使用模板时,需要指出编译器应把类或函数实例化成何种类型。

vector<int> ivec;
vector<Sales_item> Sales_vec;
vector<vector<string>> file;

3.3.1 定义和初始化vector对象

在这里插入图片描述

vector<string> svec; // 默认初始化,svec不含任何元素vector<int> ivec; // 初始化状态为空
vector<int> ivec2(ivec); // 把ivec的元素拷贝给ivec2
vector<int> ivec3 = ivec; // 把ivec的元素拷贝给ivec3
vector<string> svec(ivec2); // 错误

一、列表初始化vector对象

vector<string> articles = {"a", "an", "the"}; // 列表初始化
vector<string> v1{"a", "an", "the"}; // 列表初始化
vector<string> v2("a", "an", "the"); // 错误

二、创建指定数量的元素

vector<int> ivec(10, -1); // 10个int类型的元素,每个都被初始化为-1
vector<string> svec(10, "hi!"); // 10个string类型的元素,每个都被初始化为"hi!"

三、值初始化

vector<int> ivec(10); // 10个int类型的元素,每个都被初始化为0
vector<string> svec(10); // 10个string类型的元素,每个都是空string对象

四、列表初始值还是元素数量?

vector<int> v1(10); // v1有10个元素,每个值都是0
vector<int> v2{10}; // v2有1个元素,该元素的值10vector<int> v3(10, 1); // v3有10个元素,每个值都是1
vector<int> v4{10, 1}; // v4有2个元素,值分别是10和1
// 如果用的是圆括号,可以说提供的值是用来构造vector对象的
// 如果用的是花括号,可以表述成我们想列表初始化该vector对象,初始化过程会尽可能地把花括号内的值当成是元素初始值的列表来处理,只有在无法执行列表初始化时才会考虑其他初始化方式
// 如果初始化时使用了花括号的形式但是提供的值又不能用来列表初始化,就要考虑用这样的值来构造vector对象了vector<string> v5{"hi"}; // 列表初始化,v5有一个元素
vector<string> v6("hi"); // 错误,不能使用字符串字面值构建vector对象
vector<string> v7{10}; // v7有10个默认初始化的元素
vector<string> v8{10, "hi"}; // v8有10个值为“hi”的元素

3.3.2 向vector对象中添加元素

push_back负责把一个值当成vector对象的尾元素“压到”vector对象的“尾端”。

vector<int> v2;
for(int i = 0; i != 100; ++i)v2.push_back(i); // 循环结束后v2有100个元素,值从0到99string word;
vector<string> text;
while(cin >> word) text.push_back(word);

一、向vector对象添加元素蕴含的编程假定

如果循环体内部包含有向vector对象添加元素的语句,则不能使用范围for循环。(范围for语句体内不应该改变其所遍历序列的大小)

3.3.3 其他vector操作

==表3.5==

vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};
for (auto &i : v)i *= i;
for (auto i: v)cout << i << " ";
cout << endl;

empty检查vector对象是否包含元素然后返回一个布尔值。
size返回vector对象中元素的个数,返回值的类型是vector定义的size_type类型。

vector<int>::size_type // 正确
vector::size_type // 错误

一、计算vector内对象的索引

#include <iostream>
#include <string>
#include <vector>using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;int main()
{vector<unsigned> scores(11, 0);unsigned grade;while (cin >> grade) {if (grade <= 100)++scores[grade / 10];}for (auto s : scores)cout << s << " ";return 0;
}

二、不能用下标形式添加元素

vector对象(以及string对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素。只能对确知已存在的元素执行下标操作。

vector<int> ivec;
for (decltype(ivec.size()) ix = 0; ix != 10; ++ix)ivec[ix] = ix; // 错误,ivec是一个空vector,根本不包含任何元素,当然不能通过下标去访问任何元素vector<int> ivec;
cout << ivec[0]; // 错误

3.4 迭代器介绍

迭代器也提供了对对象的间接访问。使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另外一个元素。迭代器有有效和无效之分。有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置;其他所有情况都属于无效。

3.4.1 使用迭代器

auto b = v.begin(), e = v.end(); 
// b和e类型相同,b表示v的第一个元素,e表示v尾元素的下一位置(尾后迭代器)
// 如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器

一、迭代器运算符

执行解引用的迭代器必须合法并确实指示着某个元素。
==表3.6==

string s("sone string");
if (s.begin() != s.end()) {auto it = s.begin();*it = toupper(*it);
}

二、将迭代器从一个元素移动到另外一个元素

迭代器的递增是将迭代器“向前移动一个位置”。

for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it)*it = toupper(*it);

三、迭代器类型

迭代器类型:iterator(可读可写)和const_iterator(可读不可写)

vector<int>::iterator it;
string::iterator it2;vector<int>::const_iterator it3;
string::const_iterator it4;

四、begin和end运算符

begin和end返回的具体类型由对象是否是常量决定,如果对象时常量,begin和end返回const_iterator;如果对象不是常量,返回iterator。

vector<int> v;
const vector<int> cv;
auto it1 = v.begin(); // it1的类型是vector<int>::iterator
auto it2 = cv.begin(); // it2的类型是vector<int>::const_iterator
auto it3 = v.cbegin(); // it3的类型是vector<int>::const_iterator

五、结合解引用和成员访问操作

// 对于一个由字符串组成的vector对象,令it是该vector对象的迭代器
(*it).empty(); // 解引用it,然后调用结果对象的empty成员
*it.empty(); // 错误:试图访问it的名为empty的成员,但it是个迭代器,没有empty成员
it->empty(); // 箭头运算符把解引用和成员访问两个操作结合在一起for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it)cout << *it << endl;

六、某些对vector对象的操作会使迭代器失效

限制:
(1)不能在for循环中向vector对象添加元素
(2)任何一种可能改变vector对象容量的操作,比如push_back,都会使vector对象的迭代器失效

// 检查非空
s.begin() != s.end()
// 范围for语句等价为
for (auto beg = v.begin(), end = v.end(); beg != end; ++beg) {auto &r = *beg;
}

七、练习

// 随机数生成
srand((unsigned)time(NULL)); // 生成随机数种子
int n = rand() % 1000; // 1000以内的随机数

3.4.2 迭代器运算

在这里插入图片描述

一、迭代器的算术运算

可以令迭代器和一个整数值相加(或相减),其返回值是向前(或向后)移动了若干个位置的迭代器。
对于string或vector的迭代器来说,除了判断是否相等,还能使用关系运算符(<、<=、>、>=)对其进行比较。参与比较的两个迭代器必须合法而且指向的是同一个容器的元素(或者尾元素的下一位置)。
只要两个迭代器指向的是同一个容器中的元素或者尾元素的下一位置,就能将其相减,所得的结果是两个迭代器的距离,其类型是名为difference_type的带符号整型数。
不支持两迭代器相加。

二、使用迭代器运算

// text必须是有序的
auto beg = text.begin(), end = text.end();
auto mid = text.begin() + (end - beg) / 2;
while (mid != end && *mid != sought) {if (sought < *mid)end = mid;else beg = mid + 1;mid = beg + (end - beg) / 2;
}

3.5 数组

数组与vector:
相同点:数组也是存放类型相同的对象的容器,这些对象本身没有名字,需要通过其所在位置访问
不同点:数组的大小确定不变,不能随意向数组中增加元素。数组的维度在定义时已经确定,如果我们想更改数组的长度,只能创建一个更大的新数组,然后把原数组的所有元素赋值到新数组中区。无法像vector那样使用size函数直接获取数组的维度。如果是字符数组,可以调用strlen函数得到字符串的长度;如果是其他数组,只能使用sizeof(array)/sizeof(array[0]) 的方式计算数组的维度。

3.5.1 定义和初始化内置数组

数组是一种复合类型。数组的声明形如a[d],其中a是数组的名字,d是数组的维度。维度说明了数组中元素的个数,因此必须大于0。数组中元素的个数也属于数组类型的一部分,编译的时候维度应该是已知的(维度必须是一个常量表达式)。默认情况下,数组的元素被默认初始化。
定义数组的时候必须制定指定数组的类型,不允许用auto关键字由初始化值的列表推断类型。数组的元素应为对象,不存在引用的数组。

一、显式初始化数组元素

const unsigned sz = 3;
int ia1[sz] = {0, 1, 2}; // 含有3个元素的数组,元素值分别是0, 1, 2
int a2[] = {0, 1, 2}; // 维度是3的数组
int a3[5] = {0, 1, 2}; // 等价于a3[] = {0, 1, 2, 0, 0}
int a5[2] = {0, 1, 2}; // 错误:初始值过多
unsigned buf_size = 1024;
int ia[buf_size]; // 错误,buf_size不是常量
int ia[4 * 7 -14]; // 正确

二、字符数组的特殊性

char a1[] = {'C', '+', '+'}; // 没有空字符,维度是3
char a2[] = {'C', '+', '+', '\0'}; // 含有显式的空字符,维度是4
char a3[] = "C++"; // 自动添加表示字符串结束的空字符
const char a4[6] = "Daniel"; // 错误:没有空间存放空字符

三、不允许拷贝和赋值

int a[] = {0, 1, 2};
int a2[] = a; // 错误,不允许使用一个数组初始化另一个数组
a2 = a; // 错误:不能把一个数组直接赋值给另一个数组

四、理解复杂的数组声明

默认情况下,类型修饰符从右向左依次绑定。

int *ptrs[10]; // ptrs是含有10个整型指针的数组
int &refs[10] = ...; // 错误:不存在引用的数组
int (*Parray)[10] = &arr; // Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr; // arrRef引用一个含有10个整数的数组
int *(&arry)[10] = ptrs; // array是数组引用,该数组含有10个指针

3.5.2 访问数组元素

在使用数组下标时,通常将其定义为size_t类型。size_t是一种机器相关的无符号类型,它被设计得足够大以便能表示内存中任意对象的大小(在cstddef头文件中)

unsigned scores[11] = {};
unsigned grade;
while (cin >> grade) {if (grade <= 100)++scores[grade/10];
}for (auto i : scores)cout << i << " ";

一、检查下标的值

3.5.3 指针和数组

string nums = {"one", "two", "three"};
string *p = &nums[0]; // p指向nums的第一个元素
// 在用到数组名字的地方,编译器会自动将其替换为一个指向数组首元素的指针
string *p2 = nums; // 等价于p2 = &nums[0]// 使用数组作为一个auto变量的初始值时,推断得到的类型是指针而非数组;而decltype不会发生转换
int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
auto ia2(ia); // ia2是一个整型指针,指向ia的第一个元素
decltype(ia) ia3; // ia3是一个数组

一、指针也是迭代器

int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = arr; // p指向arr的第一个元素
++p; // p指向arr[1]
int *e = &arr[10]; // 指向arr尾元素的下一位置的指针,不能执行解引用和递增
for (int *b = arr; b != e; ++b)cout << *b << endl;

二、标准库函数begin和end

int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *beg = begin(ia); // begin函数返回指向ia首元素的指针
int *last = end(ia); // end函数返回指向ia尾元素下一位置的指针
// 这两个函数定义在iterator头文件中,命名空间std中

三、指针运算

constexpr size_t sz = 5;
int arr[sz] = {1, 2, 3, 4, 5};
int *ip = arr; // 等价于int *ip = &arr[0]
int *ip2 = ip + 4; // ip2指向arr[4]
// 给指针加上一个整数,得到的新指针仍需指向同一数组的其他元素,或者指向同一数组尾元素的下一位置
int *p = arr + sz; // p指向arr尾元素的下一位置,不要解引用!
int *p2 = arr + 10; // 错误:arr只有5个元素,p2的值未定义

两个指针相减的结果是它们之间的距离,参与运算的两个指针必须指向同一数组当中的元素,两个指针相减的结果的类型是一种名为ptrdiff_t的标准库类型,定义在cstddef头文件中。
只要两个指针指向同一个数组的元素,或者指向该数组的尾元素的下一位置,就能利用关系运算符对其进行比较。如果两个指针分别指向不相关的对象,则不能比较它们。
如果p是空指针,允许给p加上或减去一个值为0的整型常量表达式。两个空指针也允许彼此相减,结果为0。

四、解引用和指针运算的交互

int ia[] = {0, 2, 4, 6, 8};
int last = *(ia + 4); // 把last初始化为ia[4]
last = *ia + 4; // ia[0] + 4

五、下标和指针

对数组执行下标运算其实是对指向数组元素的指针执行下标运算

int ia[] = {0, 2, 4, 6, 8};
int i = ia[2]; // ia转换成指向数组首元素的指针,ia[2]得到(ia+2)所指的元素
int *p = ia;
i = *(p + 2); // 等价于ia[2]int *p = &ia[2];
int j = p[1]; // p[1]等价于*(p+1),即ia[3]
int k = p[-2]; // ia[0]

虽然标准库类型string和vector也能执行下标运算,但是数组与它们相比还是有所不同。标准库类型限定使用的下标必须是无符号类型,而内置的下标运算无此要求。

3.5.4 C风格字符串

C风格字符串不是一种类型,而是为了表达和使用字符串而形成的一种约定俗成的写法。按此习惯书写的字符串放在字符数组中并以空字符结束。

一、C标准库String函数

头文件cstring
==表3.8==
传入此类函数的指针必须指向以空字符作为结束的数组

char ca[] = {'C', '+', '+'};
cout << strlen(ca) << endl; // 严重错误:ca没有以空字符结束

二、比较字符串

const char ca1[] = "A string example";
const char ca2[] = "A different string";
if (ca1 < ca2) // 未定义的:试图比较两个无关地址
if (strcmp(ca1, ca2) < 0) // 和两个string对象的比较s1<s2效果一样

三、目标字符串的大小由调用者指定

strcpy(largeStr, ca1); // 把cal拷贝给largeStr
strcat(largeStr, " "); // 在largeStr的末尾加上一个空格
strcat(largeStr, ca2); // 把ca2连接到largeStr后面

3.5.5 与旧代码的接口

一、混用string对象和C风格字符串

1、允许使用字符串字面值来初始化string对象
2、允许使用以空字符结束的字符数组来初始化string对象或为string对象赋值
3、在string对象的加法运算中允许使用以空字符结束的字符数组作为其中一个运算对象(不能两个运算对象都是);在string对象的复合赋值运算中允许使用以空字符结束的字符数组作为右侧的运算对象

string s("Hello World");
char *str = s; // 错误:不能用string对象初始化char*
const char *str = s.c_str(); 
// c_str()函数的返回值是一个C风格的字符串,函数的返回结果是一个指针,该指针指向一个空字符结束的字符数组,而这个数组所存的数据恰好与那个string对象的一样
// str指向s的内存单元,并未将s的内容进行拷贝

二、使用数组初始化vector对象

int int_arr[] = {0, 1, 2, 3, 4, 5};
vector<int> ivec(begin(int_arr), end(int_arr)); // ivec有6个元素,分别是int_arr中对应元素的副本
vector<int> subVec(int_arr+1, int_arr+4); // 拷贝3个元素int_arr[1]、int_arr[2]、int_arr[3]

3.6 多维数组

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

int ia[3][4]; // 大小为3的数组,每个元素是含有4个整数的数组
int arr[10][20][30] = {0}; // 大小为10的数组,它的每个元素都是大小为20的数组,这些数组的元素是含有30个整数的数组

一、多维数组的初始化

int ia[3][4] = {{0, 1, 2, 3},{4, 5, 6, 7},{8, 9, 10, 11}
};
int ia[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
int ia[3][4] = {{0}, {4}, {8}}; // 显式地初始化每行的首元素

二、多维数组的下标引用

如果表达式含有的下标运算符数量和数组的维度一样多,该表达式的结果将是给定类型的元素;反之,如果表达式含有的下标运算符数量比数组的维度小,则表达式的结果将是给定索引处的一个内层数组。

ia[2][3] = arr[0][0][0];
int (&row)[4] = ia[1]; // 把row绑定到ia的第二个4元素数组上
constexpr size_t rowCnt = 3, colCnt = 4;
int ia[rowCnt][colCnt];
for (size_t i = 0; i != rowCnt; ++i) {for (size_t j = 0; j != colCnt; ++j) {ia[i][j] = i * colCnt + j;}
}

三、使用范围for语句处理多维数组

size_t cnt = 0;
for (auto &row : ia) {for (auto &col : row) {col = cnt;++cnt;}
}// 多维数组使用范围for循环时,只能使用引用
for (auto row : ia)for (auto col : row)
// 错误:第一个循环遍历ia的所有元素,这些元素实际上是大小为4的数组,因为row不是引用类型,所以编译器初始化row时会自动将这些数组形式的元素转换成指向数组内首元素的指针,这样得到的row的类型就是int*,显然不合法

四、指针和多维数组

// 因为多维数组实际上是数组的数组,所以由多维数组名转换得来的指针实际上是指向第一个内层数组的指针
int ia[3][4];
int (*p)[4] = ia; // p指向含有4个整数的数组
p = &ia[2]; // p指向ia的尾元素
for (auto p = ia; p != ia + 3; ++p) {for (auto q = *p; q  != *p + 4; ++q) { // *p是一个含有4个整数的数组cout << *q << ' ';}
}for (auto p = begin(ia); p != end(ia); p++) {for (auto q = begin(*p); q != end(*p); q++) {cout << *q << ' ';}
}

五、类型别名简化多维数组的指针

using int_array = int[4];
tydefef int int_array[4];
for (int_array *p = ia; p != ia + 3; ++p) {for (int *q = *p; q != *p + 4; ++q) {cout << *q << ' ';}
}

六、练习

#include <iostream>
#include <iterator>using std::cin;
using std::cout;
using std::endl;
using std::begin;
using std::end;int main()
{int ia[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};for (int (&p)[4] : ia) {for (int &q : p) {cout << q << " ";}cout << endl;}for (int i = 0; i < 3; i++) {for (int j = 0; j < 4; j++) {cout << ia[i][j] << " ";}cout << endl;}for (int (*p)[4] = ia; p != ia + 3; p++) {for (int *q = *p; q != *p + 4; q++) {cout << *q << " ";}cout << endl;}return 0;
}

小结

string对象和vector是两种最重要的标准库类型。string对象是一个可变长的字符序列,vector对象是一组同类型对象的容器。
迭代器允许对容器中的对象进行间接访问,对于string对象和vector对象来说,可以通过迭代器访问元素或者在元素间移动。
数组和指向数组元素的指针在一个较低的层次上实现了与标准库类型string和vector类似的功能。一般来说,应该优先选用标准库提供的类型,之后再考虑C++语言内置的低层的替代品数组或指针

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_166205.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CVPR2022 Semi-Supervised Semantic Segmentation Using Unreliable Pseudo-Labels

Semi-Supervised Semantic Segmentation Using Unreliable Pseudo-Labels 使用不可靠的伪标签的半监督语义分割 Paper&#xff1a;https://openaccess.thecvf.com/content/CVPR2022/html/Wang_Semi-Supervised_Semantic_Segmentation_Using_Unreliable_Pseudo-Labels_CVPR_202…

Python 没有 pip 包问题解决

最近需要搞一个干净的Python,从官网上直接下载解压可用的绿色版&#xff0c;发现无法正常使用PiP 一 官网下载Python https://www.python.org/downloads/ 选择 embeddable package,这种是免安装的包&#xff0c;解压后可以直接使用。 二 配置环境变量 添加环境变量&#xff1a…

CXL.mem M2S Message 释义

&#x1f525;点击查看精选 CXL 系列文章&#x1f525; &#x1f525;点击进入【芯片设计验证】社区&#xff0c;查看更多精彩内容&#x1f525; &#x1f4e2; 声明&#xff1a; &#x1f96d; 作者主页&#xff1a;【MangoPapa的CSDN主页】。⚠️ 本文首发于CSDN&#xff0c…

基础算法--快速排序

快速排序 算法原理 1. 取一个元素p(第一个元素&#xff0c;最后一个元素&#xff0c;中间元素&#xff0c;随机 都可以)&#xff0c;使元素p归位。 2. 列表被p分成两部分&#xff0c;左边都比p小&#xff0c;右边都比p大。 3. 递归完成排序。 动态演示 python代码实现 import…

思维的深度,决定职场的高度

经常有读者问我&#xff0c;自己做事很努力&#xff0c;可是结果却总是不尽如人意&#xff0c;问题究竟出在哪里&#xff1f; 虽然成事的关键因素有很多&#xff0c;但是归根结底其实只有两点&#xff0c;就是做局和破局。也就是&#xff0c;如何识破别人给你做的局&#xff1f…

Feign负载均衡写法

Feign主要为了面向接口编程 feign是web service客户端&#xff0c;是接口实现的&#xff0c;而ribbon是通过微服务名字访问通过RestTemplate调用的&#xff0c;如下&#xff1a; 在Feign的实现下&#xff0c;我们只需要创建一个接口并使用注解的方式来配置它&#xff08;类似…

vue声明周期

1.在created中发送数据 async created(){ const resawait axios.get("url) this.listres.data.data } 2.在mounted中获取焦点 mounted(){ document.querySelector(#inp).focus()

JavaScript基础05——字面量、变量介绍及变量基本使用

哈喽&#xff0c;大家好&#xff0c;我是雷工&#xff01; 说起变量感觉很熟悉&#xff0c;但要让解释什么是变量时&#xff0c;却有点语塞&#xff0c;就像解释下为啥112一样&#xff0c;感觉非常熟悉&#xff0c;就是知道&#xff0c;但确解释不出来。 不过虽然在其他场景比较…

【无标题】嵌入式开发-IIC通信介绍

IIC&#xff08;Inter-Integrated Circuit&#xff09;是一种两线式串行总线协议&#xff0c;用于连接微控制器及其他外围设备。在IIC总线上的数据传输速率可以是标准模式&#xff08;100Kbit/s&#xff09;&#xff0c;快速模式&#xff08;400Kbit/s&#xff09;和高速模式&a…

用 ChatGPT 写代码太省时间了

几个月前&#xff0c;我们聊过陶哲轩使用 ChatGPT 辅助解决数学问题。当时&#xff0c;他觉得虽然测试结果不太令人满意&#xff0c;但也并没有对 ChatGPT 持完全否定的态度。他觉得&#xff0c;像 ChatGPT 这类大型语言模型在数学中可以用来做一些半成品的语义搜索工作&#x…

【项目经验】:elementui表格中表头的多选框换成文字

一.项目需求 表格可以多选&#xff0c;表头都是汉字。。。。类似于这种 二.实现功能 用到的方法 Table Attributes 参数说明类型可选值默认值header-cell-class-name表头单元格的 className 的回调方法&#xff0c;也可以使用字符串为所有表头单元格设置一个固定的 className。…

3、Spring 之IOC 容器 详解

IoC 是 Inversion of Control 的简写&#xff0c;译为“控制反转”&#xff0c;它不是一门技术&#xff0c;而是一种设计思想&#xff0c;是一个重要的面向对象编程法则&#xff0c;能够指导我们如何设计出松耦合、更优良的程序。 Spring 通过 IoC 容器来管理所有 Java 对象的…

【斗罗Ⅱ】最强武魂揭秘,98级玄老、95级言少哲神兽级武魂曝光

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析【绝世唐门】 在斗罗大陆动画绝世唐门中&#xff0c;98级玄老已经登场&#xff0c;他是一个很随意的老人&#xff0c;乍眼一看&#xff0c;似乎是一个邋里邋遢、好吃懒做的人&#xff0c;但是实际上他却是史莱克学院重量级…

载入qss时出现Could not parse application stylesheet

我这里其实qss文件本身没有错误。 参考&#xff1a;解决Qt Creator修改qss文件后导致样式无效问题_qt qss改变但运行结果没变_风吹沙走的博客-CSDN博客 我的解决方法&#xff1a; (1)UTF-8 BOM:总是删除 (2) 文本重新编码为ANSI 这时候中文会变成乱码。 (3)我事先复制了一…

同步与互斥

硬件指令 实现互斥&#xff1a;硬件指令&#xff0c;硬件实现的原子操作&#xff0c;不会被打断 tsl指令和xchg指令 当前指令执行完&#xff0c;才会检测中断 If the signal comes while an instruction is being executed, it is held until the execution of the instructi…

【javaweb】学习日记Day8 - Mybatis入门 Mysql 多表查询 事务 索引

之前学习过的SQL语句笔记总结戳这里→【数据库原理与应用 - 第六章】T-SQL 在SQL Server的使用_Roye_ack的博客-CSDN博客 【数据库原理与应用 - 第八章】数据库的事务管理与并发控制_一级封锁协议_Roye_ack的博客-CSDN博客 目录 一、多表查询 1、概述 &#xff08;1&#…

时序预测 | MATLAB实现CNN-GRU卷积门控循环单元时间序列预测(风电功率预测)

时序预测 | MATLAB实现CNN-GRU卷积门控循环单元时间序列预测&#xff08;风电功率预测&#xff09; 目录 时序预测 | MATLAB实现CNN-GRU卷积门控循环单元时间序列预测&#xff08;风电功率预测&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.时序预测 | MA…

一键导出文件名和位置,让你轻松管理文件!

想要轻松管理你的文件吗&#xff1f;试试我们的文件名和位置导出工具&#xff0c;一键导出文件名和位置&#xff0c;让你轻松管理你的文件&#xff01;我们的工具可以在不修改文件名的前提下&#xff0c;快速导出文件名和位置&#xff0c;让你随时随地查找和管理你的文件。 第…

嵌入式开发-SPI通信介绍

SPI&#xff08;Serial Peripheral Interface&#xff09;是一种串行外设接口规范&#xff0c;它是由摩托罗拉公司制定的一种通讯协议。它广泛应用于微控制器、存储器和其他外设之间的通信。 SPI是一种同步串行通信协议&#xff0c;它支持四线通信&#xff1a; SCK&#xff0…

【数学建模竞赛】Matlab逻辑规则,结构基础及函数

逻辑基础 逻辑变量 在Matlab中&#xff0c;逻辑变量是一种特殊类型的变量&#xff0c;用于表示逻辑值。逻辑变量只有两个可能的值&#xff1a;true&#xff08;真&#xff09;和false&#xff08;假&#xff09;。在Matlab中&#xff0c;我们可以使用0和1来表示逻辑变量的值。…