IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C++】C++ 基础——字符串、向量和数组 -> 正文阅读

[C++知识库]【C++】C++ 基础——字符串、向量和数组

参考资料:C++ Primer 中文版(第5版)——[美] Stanley B. Lippman [美] Josée Lajoie [美] Barbara E. Moo 著 王刚 杨巨峰 译

代码编辑器:VS 2019


除了内置类型之外,C++ 语言还定义了一个内容丰富的抽象数据类型库。其中,stringvector是两种最重要的标准库类型,前者支持可变长字符串后者则表示可变长的集合。还有一种标准库类型是迭代器,它是stringvector的配套类型,常被用于访问string中的字符或vector中的元素

内置数组是一种更基础的类型,stringvector都是对它的某种抽象。

和其他内置类型一样,数组的实现与硬件密切相关。因此相较于标准库类型stringvector,数组在灵活性上稍显不足

1. 命名空间的 using 声明

这里介绍访问库中名字的简单方法。

前面用到的比如std::cin就表示从标准输入中读取内容。库函数cin就属于命名空间std。我们用到的是作用域操作符(::),它的含义是:编译器应从操作符左侧名字所示的作用域中寻找右侧那个名字。因此,std::cin的意思就是要使用命名空间std中的名字cin

还有一种简单的途径使用到命名空间中的成员,就是使用using声明,同时这也是最安全的方法。

有了using声明就无需专门的前缀(形如命名空间::)也能使用所需的名字了。using声明具有如下的形式:

using namespace::name;

一旦声明了上述语句,就可以直接访问命名空间中的名字。

注意每个名字都需要独立的 using 声明。因为按照规定,每个using声明引入命名空间中的一个成员。例如,可以把要用到的标准库中的形式表示出来:

#include<iostream>

using std::cin; using std::cout;
//上面一行写成 using std::cin, std::cout 或者 using std::cin, cout 都是错误的
int main() {
	int a;
	cin >> a;
	cout << a;
	return 0;
}

C++ 语言比较自由,因此既可以一行只放一条using声明语句,也可以一行放上多条。不过要注意,用到的每个名字都必须有自己的声明语句,而且每句话都得以分号结束

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

2. 标准库类型 string

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

2.1 定义和初始化 string 对象

初始化string对象的方式如下表所示:

序号格式说明
1string s1默认初始化,s1是个空串
2string s2(s1)s2s1的副本
3string s2 = s1等价于方式3
4string s3("value")s3是字面值"value"的副本(但不包括末尾的空字符)
5string s3 = "value"等价于方式4
6string s4(n, 'c')s4初始化为由连续n个字符c组成的串

注意上面的方式4和方式5,我们提供了一个字符串字面值来初始化string对象,字面值中除了最后那个空字符外其他所有的字符都被拷贝到新创建的string对象中去。

下面区分一下直接初始化拷贝初始化

如果使用等号=初始化一个变量,实际上执行的是拷贝初始化(copy initialization),编译器把等号右侧的初始值拷贝到新创建的对象中去。

与之相反,如果不使用等号,则执行的是直接初始化(direct initialization)。

当初始值只有一个时,使用直接初始化或拷贝初始化都行。如果初始化要用到的值有多个,一般来说只能使用直接初始化的方式。

2.2 string 对象上的操作

一个类除了要规定初始化其对象的方式外,还要定义对象上所能执行的操作。类既能定义通过函数名调用的操作,也能定义<<+等各种运算符在该类对象上的新含义

一些常用的操作如下表:

操作说明
os << ss写到输出流os当中,返回os
is >> sis中读取一行赋给s,返回is
getline(is, s)is中读取一行赋给s,返回is
s.empty()s为空返回true,否则返回false
s.size()返回s中字符的个数
s[n]返回s中第n个字符的引用,位置n0记起
s1 + s2返回s1s2连接后的结果
s1 = s2s2的副本代替s1中原来的字符
s1 == s2判断所含字符是否完全一样(对大小写敏感)
s1 != s2判断所含字符是否不一样
<, <=, >, >=利用字符在字典中的顺序进行比较(对大小写敏感)

注意,使用cin读取内容时,会自动忽略开头的空白,从第一个真正的字符开始读起,直到遇见下一个空白符为止

所以想要保留输入时的空白符时,应该用getline函数代替原来的>>运算符getline函数的参数是一个输入流和一个string对象,函数从给定的输出流中读入内容,直到遇到换行符为止(注意换行符也被读进来了),然后把所读的内容存入到那个string对象中去(注意不存换行符)。getline只要一遇到换行符就结束读取操作并返回结果,哪怕输入的一开始就是换行符也是如此。如果输入真的一开始就是换行符,那么所得的结果是个空string

关于上表中的"返回is"这一点,指的是返回流参数,可以参考这两篇文章:C++ cin>>n 的返回值C++ IO库条件状态。这一点可以用在while循环当中,当读到EOF^Z时会退出循环,比如下面的例子:

//将每行输入写入名为"test.txt"的文件当中去
#include<iostream>
#include<string>
#include<fstream>

int main() {
    std::ofstream outFile("test.txt");
    std::string line;
    while (getline(std::cin, line)) {
        outFile << line << std::endl;
    }
    return 0;
}

比如我们输入:

Hello, world! 2021/8/26
Hello, C++! Aug.26 2021
^Z

那么在源文件所在文件夹中便会出现一个名为test.txt的文本文件,里面便存放着我们刚刚输入的内容。

再说明一下size函数的返回值类型。这个函数的返回值类型并非我们预料的那样是intunsigned,而是一个string::size_type类型的值。很清楚的一点是它是个无符号类型的值而且足够存放下任何string对象的大小。所有用于存放string类的size函数返回值的变量,都应该是string::size_type类型的。所以当我们需要遍历string对象时,假如这里我们使用for循环,那么建议自增的索引的类型声明为decltype(s.size()),如下例:

std::string s = "hello, world!";
for(decltype(s.size()) i = 0; i < s.size(); i++){
    std::cout << s[i] << ' ';
}
//最后的输出是:h e l l o , w o r l d ! 

最后再说明一下字面值和string对象相加

当把string对象和字符字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string。下面举一些正确和错误的例子:

std::string s1 = "hello";
std::string s2 = s1 + ",";		//正确
std::string s3 = "hello" + ",";	//错误:两个运算对象都不是string

//正确:等价于 (s1 + ",") + "world",所以是正确的
std::string s4 = s1 + "," + "world"; 

//错误:等价于 ("hello" + ",") + s2,所以是错误的
std::string s5 = "hello" + "," + s2;

切记:字符串字面值与string是不同的类型

2.3 处理 string 对象中的字符

cctype头文件中定义了一组标准库函数处理字符,如下表:

函数说明
isalnum(c)c字母或数字时为真
isalpha(c)c字母时为真
iscntrl(c)c控制字符时为真
isdigit(c)c数字时为真
isgraph(c)c不是空格但可打印时为真
islower(c)c小写字母时为真
isprint(c)c可打印字符(空格或具有可视形式)时为真
ispunct(c)c标点符号(不是控制字符、数字、字母、可打印空白中的一种)时为真
isspace(c)c空白(空格、横向制表符、纵向制表符、回车符、换行符、进纸符)时为真
isupper(c)c大写字母时为真
isxdigit(c)c十六进制数时为真
tolower(c)如果c是大写字母,输出对应的小写字母;否则原样输出c
toupper(c)如果c是小写字母,输出对应的大写字母;否则原样输出c

下面介绍一下范围 for 语句

范围for语句可以遍历给定序列中的每个元素并对序列中的每个值执行每个操作,其语法形式是:

for(declaration : expression)
    statement

其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。

一个string对象表示一个字符的序列,因此string对象可以作为范围for语句中的expression部分。遍历方法如下:

std::string s = "hello, world";
for(auto c : s){
    //...
}

比如我们写一个判断一个string对象中标点符号的个数的程序:

#include<iostream>
#include<string>
#include<cctype>

int main() {
    std::string s = "<Jan.><Mar.><May.><Jul.><Sep.><Nov.>";
    int n = 0;
    for (auto c : s) {
        if (ispunct(c))
            n++;
    }
    std::cout << n << std::endl;
    return 0;
}
//运行之后,结果输出 18

再比如,我们修改上面的程序,将字符串中的尖括号<>换成[]

#include<iostream>
#include<string>

int main() {
    std::string s = "<Jan.><Mar.><May.><Jul.><Sep.><Nov.>";
    for (auto &c : s) {//因为要对原字符串进行修改,所以改用引用类型
        if (c == '<')
            c = '[';
        if (c == '>')
            c = ']';
    }
    std::cout << s << std::endl;
    return 0;
}
//运行之后,输出结果:[Jan.][Mar.][May.][Jul.][Sep.][Nov.]

注意,上面用的是引用类型auto &c : s,因此对c的修改即为对string对象修改。

3. 标准库类型 vector

标准库类型vector表示对象的集合,其中所有对象的类型都相同。集合中的每个对象都有一个与之对应的索引,索引用于访问对象。因为vector “容纳” 着其他对象,所以它也常被称作容器(container)。

要想使用vector,必须包含vector头文件。

C++ 既有类模板(class template)也有函数模板,其中vector是一个类模板。

模板本身不是类或函数,相反可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为实例化(instantiation),当使用模板时,需要指出编译器应把类或函数实例化成何种类型。

对于类模板来说,我们通过提供一些额外信息来指定模板到底实例化成什么样的类,需要提供哪些信息由模板决定。提供信息的方式总是这样:即在模板名字后面跟一对尖括号,在括号内放上信息

vector为例,提供的额外信息是vector内所存放对象的类型:

vector<int> iv;						//iv 保存 int 类型的对象
vector<std::string> strv;			//strv 保存 string 类型的对象
vector<vector<std::string>> file;	//该容器中的对象是 vector 对象,还是容器

尖括号中可以放各种数据类型,比如自己定义的类、结构体等等。

vector能容纳绝大多数类型的对象作为其元素。但是因为引用不是对象,所以不存在包含引用的vector。除此之外,其他大多数内质类型和类类型都可以构成vector对象,甚至组成vector元素也可以是vector

3.1 定义和初始化vector对象

初始化vector对象的常用方法如下表所示:

序号格式说明
1vector<T> v1v1是一个空vector,它潜在的元素是T类型,执行默认初始化
2vector<T> v2(v1)v2中包含有v1所有元素的副本
3vector<T> v2 = v1等价于方法2
4vector<T> v3(n, val)v3包含了n个重复的元素,每个元素的值都是val
5vector<T> v4(n)v4包含了n个重复地执行了值初始化的对象
6vector<T> v5{a,b,c...}v5包含了初始值个数的元素,每个元素被赋予相应的初始值
7vector<T> v5={a,b,c...}等价于方法6

这里再强调一下初始化方式的特殊要求:

  • (1)使用拷贝初始化时(即使用=时),只能提供一个初始值;
  • (2)如果提供的是一个类内初始值,则只能使用拷贝初始化或使用花括号的形式初始化;
  • (3)如果提供的是初始元素的列表,则只能把初始值都放在花括号里进行列表初始化,而不能放在圆括号里。

注意下面容易混淆的初始化例子:

vector<int> v1(10);			//v1 有 10 个元素,每个值都是 0
vector<int> v2{10};			//v2 有 1 个元素,该元素的值是 10

vector<int> v3(10, 1);		//v3 有 10 个元素,每个值都是 1
vector<int> v4{10, 1};		//v4 有两个元素,值分别是 10 和 1

请记住,如果用的是圆括号,则表示提供的值是用来构造(construct)vector对象的。如果用的还花括号,则表示我们想列表初始化(list initialize)该vector对象。

但是,请注意,如果初始化时使用了花括号的形式但提供的值又不能用来列表初始化,就要考虑用这样的值来构造vector对象了。如下例所示:

vector<string> v1{"hi"};		//列表初始化,v1 只有一个元素
vector<string> v2("hi");		//错误:不能使用字符串字面值构建 vector 对象
vector<string> v3{10};			// v3 有 10 个默认初始化的元素
vector<string> v4{10, "hi"};	//v4 有 10 个值为 "hi" 的元素

即在编译器确认无法执行列表初始化后,编译器会尝试使用默认值初始化vector对象。

3.2 vector 对象上的操作

vector对象来说,直接初始化的方式适用于三种情况:初始值已知且数量较少、初始值是另一个vector对象的副本、所有元素的初始值都一样。然而更常见的情况是,创建一个vector对象时并不清楚实际所需的元素个数,元素的值也经常无法确定。所以向vector对象中添加元素是一种很重要的操作。除了添加操作,还有一些其他很常用的操作,一并列入下表:

格式说明
v.push_back(t)v的尾端添加一个值为t的元素
v.pop_back()弹出(即删除)v尾端的一个元素
v.size()返回v中元素的个数
v.empty()判断v是否为空——是则返回true,否则返回false
v[n]返回v中第n个位置上元素的引用
v1 = v2v2中元素的拷贝替换v1中的元素
v1 = {a,b,c...}用列表中元素的拷贝替换v1中的元素
v1 == v2判断v1v2是否相等
v1 != v2判断v1v2是否不相等
<, <=, >, >=以字典顺序进行比较

首先需要注意的是不能通过下标形式向vector中添加元素,要么在初始化时添加,要么通过push_back()函数添加

其次需要说明的是,v.size()的返回值和前面的string类型的大同小异,它的返回值类型是vector<T>::size_type

最后再解释一下上面v[n]的说明中"返回引用"。返回引用即表示对v[n]进行修改即是对vector中的元素进行修改。

4. 迭代器介绍

我们已经知道可以使用下标运算符来访问string对象的字符或vector对象的元素,还有另外一种更通用的机制也可以实现同样的目的,这就是迭代器(iterator)。C++ 除了vector之外,标准库还定义了其他几种标准器。所有标准库容器都可以使用迭代器,但是其中只有少数几种才同时支持下标运算符

严格来说,string对象不属于容器类型,但是string支持很多与容器类似的操作。vector支持下标运算符,这点和string一样;string支持迭代器,这也和vector是一样的。

类似于指针类型,迭代器也提供了对对象的间接访问。就迭代器而言,其对象是容器中的元素或者string对象中的字符。使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另外一个元素迭代器有有效和无效之分,这一点和指针差不多。有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置;其他所有情况都属于无效

4.1 使用迭代器

和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员。比如,这些类型都拥有名为beginend的成员,其中begin成员负责返回指向第一个元素(或第一个字符)的迭代器。end成员则负责返回指向容器(或string对象)“尾元素的下一个位置(one past the end)” 的迭代器,也就是说,该迭代器指示的是容器的一个本不存在的尾后元素(off the end)。这样的迭代器没什么实际含义,仅是个标记而已,表示我们已经处理完了容器中的所有元素。end成员返回的迭代器常被称作尾后迭代器(off-the-end iterator)或者简称为尾迭代器(end itertor)。特殊情况下如果容器为空,则beginend返回的是同一个迭代器。

请看下例:

#include<iostream>
#include<string>
#include<typeinfo>

int main() {
    std::string str = "hello";
    auto x = str.begin();
    std::cout << typeid(x).name() << std::endl;
    return 0;
}

运行之后,输出结果:

class std::_String_iterator<class std::_String_val<struct std::_Simple_types<char> > >

这便是str.begin()这个迭代器的数据类型。

4.1.1 迭代器运算符

下表列出迭代器支持的运算:

格式说明
*iter返回迭代器iter所指元素的引用
iter->mem解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem
++iteriter指示容器中的下一个元素
--iteriter指示容器的上一个元素
iter1 == iter2判断两个迭代器是否相等
iter1 != iter2判断两个迭代器是否不相等

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

关于判断两迭代器是否相等的说明:如果两个迭代器指示的是同一个元素或者它们是同一个容器的尾后迭代器则相等;反之不相等。

再看一个上面出现过的把尖括号改成中括号的例子,我们用迭代器来实现:

int main() {
    std::string s = "<Jan.><Mar.><May.><Jul.><Sep.><Nov.>";
    for (auto iter = s.begin(); iter != s.end(); iter++) {
        if (*iter == '<')
            *iter = '[';
        if (*iter == '>')
            *iter = ']';
    }
    std::cout << s << std::endl;
    return 0;
}

输出结果和之前一样:

[Jan.][Mar.][May.][Jul.][Sep.][Nov.]

4.1.2 迭代器类型

就像不知道stringvectorsize_type成员到底是什么类型一样,一般来说我们也不知道(其实是无须知道)迭代器的精确类型。而实际上,那些拥有迭代器的标准库类型使用iteratorconst_iterator来表示迭代器的类型

vector<int>::iterator it;	//it 能读写 vector<int> 的元素
string::iterator it2;		//it2 能读写 string 对象中的字符

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

const_iterator和常量指针差不多,能读取但不能修改它所指的元素值。相反,iterator的对象可读可写。

4.1.3 begin 和 end 运算符

beginend返回的具体类型由对象是否是常量决定,如果对象是常量,beginend返回const_iterator;如果对象不是常量,返回iterator

如果对象只需读操作而无须写操作的话最好使用常量类型。为了专门得到const_iterator类型的返回值,C++11 新标准引入了两个新函数,分别是cbegincend

auto it = v.cbegin();
//it 的类型是 vector<int>::const_iterator

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

解引用迭代器可获得迭代器所指的对象,如果该对象的类型恰好是类,就有可能希望进一步访问它的成员

如下例:

(*it).f();	//解引用之后调用成员函数
it -> f();	//同上,但表达更方便

箭头运算符即把解引用和成员访问两个操作结合在一起——it -> mem(*it).mem表达的意思相同

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

虽然vector对象可以动态地增长,但是也会有一些副作用。已知的一个限制是不能在范围for循环中向vector对象添加元素另外一个限制是任何一种可能改变vector对象容量的操作(比如push_back)都会使该vector对象的迭代器失效

谨记,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素

4.2 迭代器运算

迭代器的递增运算令迭代器每次移动一个元素,所有的标准库容器都有支持递增运算的迭代器。类似的,也能用==!=对任意标准库类型的两个有效迭代器进行比较。

srtingvector的迭代器提供了更多额外的运算符,一方面可使得迭代器的每次移动跨过多个元素,另外也支持迭代器进行关系运算。srtingvector迭代器支持的运算如下表所示:

运算说明
iter + n迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置与原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置
iter - n+大同小异,不再赘述
iter += n复合赋值语句
iter -= n复合赋值语句
iter1 - iter2两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后将得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置
>、>=、<、<=迭代器的关系运算符,如果某迭代器指向的容器位置在另一个迭代器所指位置之前,则说前者小于后者。参与运算的两个迭代器必须指向的是同一容器中的元素或者尾元素的下一位置

下面给出使用迭代器运算的经典算法——二分搜索

首先搜索的对象必须是有序的,这里我们假设对象是递增的,给出算法如下:

//搜索对象是容器 v,假设它里面的元素是递增排列的
//beg 和 end 代表搜索的范围
auto beg = v.begin(), end = v.end();
auto mid = v.begin() + (end - beg) / 2;
//要找的元素是 sought
//当还有元素尚未检查并且我们还没有找到 sought 时执行循环
while(mid != end && *mid != sought){
    if(sought < *mid)//如果要找的元素在前半部分
        end = mid;
    else			//否则要找的元素在后半部分
        beg = mid + 1;
    mid = beg + (end - beg) / 2;	//更新中间点
}

再给出一个具体的例子如下:

#include<iostream>
#include<vector>

int main() {
    std::vector<int> numArr{ 1,3,5,7,9,11,13,14,15,17,19,21,
                            23,24,25,27,29,31,33,35,37,39,40 };
    int sought;
    std::cin >> sought;//要找的值
    auto beg = numArr.begin(), end = numArr.end();
    auto mid = numArr.begin() + (end - beg) / 2;    //初始状态下的中间点
    //当还有元素尚未检查并且我们还没有找到 sought 时执行循环
    while (mid != end && *mid != sought) {
        if (sought < *mid)
            end = mid;
        else
            beg = mid + 1;
        mid = beg + (end - beg) / 2;
    }
    if (*mid == sought)		//如果找到了输出下标
        std::cout << mid - numArr.begin() << std::endl;
    else		//否则输出未找到的信息
        std::cout << "Not found!" << std::endl;
    return 0;
}

5. 数组

由于 C语言中已经出现了数组这个类型,所以这里仅列出 C++ 中数组方面的新知识点。

  1. 引用数组:

    int arr[2] = {0, 1};
    int (&arrRef)[2] = arr;	//arrRef 引用一个含有 2 个整数的数组
    //注意:下面的声明方法是错误的
    int &arrRef2[2] = arr;
    //这种声明其实相当于声明了一个引用数组,但是引用并不是对象,所以不存在引用的数组
    

    要想理解数组声明的含义,最好的办法是从数组的名字开始按照由内向外的顺序阅读

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

  3. 遍历数组的所有元素也可以采用范围for语句。

  4. C++11 新标准引入的跟指针相关的两个标准库函数beginend。这两个函数与容器中的两个同名成员功能类似,不过数组不是类类型,因此这两个函数不是成员函数,所以使用形式也不太相同:

    int ia[] = {0,1,2,3,4,5,6};
    int *beg = begin(ia);
    int *last = end(ia);
    

    begin函数返回指向ia首元素的指针,end函数返回指向ia尾元素下一位置的指针,这两个函数定义在iterator头文件中。

  5. 两个指向数组的指针相减的结果的类型是一种名为ptrdiff_t的标准库类型,和size_t一样,ptrdiff_t也是一种定义在cstddef头文件中的机器相关的类型。因为差值可能为负值,所以ptrdiff_t是一种带符号类型。

  6. 关于混用string对象和 C 风格字符串

    有以下性质:

    • 允许使用以空字符结束的字符数组来初始化string对象或为string对象赋值
    • string对象的加法运算中允许使用以空字符结束的字符数组作为其中一个运算对象(不能两个对象都是)

    但上述性质反过来就不成立了,如果程序的某处需要一个 C 风格字符串,无法直接用string对象来代替它。为了完成该功能,string专门提供了一个名为c_str的成员函数:

    string s = "hello, world";
    char *str = s;	//错误:不能用 string 对象初始化 char*
    const char *str = s.c_str();	//正确
    

    顾名思义,c_str函数的返回值是一个 C 风格的字符串。也就是说,函数的返回结果是一个指针,该指针指向一个以空字符结束的字符数组,而这个数组所存数据恰好与那个string对象的一样。

  7. 使用数组初始化vector对象。要实现这一目的,只需指明要拷贝区域的首元素地址和尾后地址就可以了:

    int int_arr[] = {0,1,2,3,4};
    vector<int> ivec(begin(int_arr), end(int_arr));
    

    当然,使用数组的一部分初始化vector对象也是可以的,提供起始地址和中指地址后一个地址即可。

  8. 要使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型

  9. 可以使用类型别名简化多维数组的指针。如下例:

    #include<iostream>
    
    int main() {
        using int_array = int[3];	//新标准下类型别名的声明
        int a[3][3] = { {1,2,3}, {4,5,6}, {7,8,9} };
        //输出 a 中每个元素的值,每个内层数组各占一行
        for (int_array *p = a; p != a + 3; ++p) {
            for (int *q = *p; q != *p + 3; ++q)
                std::cout << *q << ' ';
            std::cout << std::endl;
        }
        return 0;
    }
    

C++ 基础——字符串、向量和数组的内容到这里就结束了。

下一篇预告:【C++】C++ 基础——表达式和语句

链接指路:

上一篇:C++ 基础——变量和基本类型

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-29 08:54:26  更:2021-08-29 08:55:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 21:28:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计