C++primer 第三章 字符串、向量和数组

第三章 字符串、向量和数组

3.1 命名空间的 using 声明

using 声明的具体形式:using namespace :: name;
有了using 声明就没必要在写单独的命名空间前缀了,可以直接写所需的名字,但是每个名字都要有单独的 using 声明。

#include <iostream>

using std::cin;
using std::cout;
using std::endl;

int main() {
    int v1, v2;
    cin >> v1 >> v2;
    cout << v1 + v2 << endl;
    return 0;
}

但是在头文件不应该包含 ```using`` 声明,因为头文件中的内容会拷贝到所有引用他的文件中,可能会造成名字冲突。

3.2标准库类型

3.2.1定义和初始化 string 对象

string对象初始化的几种方法:

string s1;  //默认是空字符串
string s2 = s1;
string s3("value"); //直接初始化
string s3 = "value; //拷贝初始化,等价于string s3("value")
string s4(10,'c');  //s4的内容是cccccccccc

3.2.2string对象上的操作

使用 getline读取一整行

int main() {
    string line;
    while(getline(cin, line))
        cout << line << endl;
    return 0;
}

string::size_type类型
string::size_type是一个无符号类型的值 auto len = line.size(); //len的类型是string::size_type
size函数返回的是无符号类型的值,所以使用size的时候避免使用int,避免int和Unsigned混用带来的问题

字面值和string对象相加

string s1 = "hello", s2 = "world";

string s3 = s1 + s2;    //+的使用必须保证+两边至少有一个是string对象
s1 += s2;   //相当于s1 = s1 + s2;
string s4 = s1 + ',';   //正确,s1是string对象
string s5 = "hello" + ",";  //错误,两个都不是string对象
string s6 = s1 + "," + "world"; //正确,相当于(s1 + ",") + "world",每个+两边都有一个string对象
string s7 = "," + "world" + s1; //错误,相当于("," + "world") + s1, 连个字面值不能直接相加

注意: C++为了和C兼容,C++中的字符串字面值和string是不同的类型。

3.2.3处理string对象中的字符

遍历string对象中的每个字符可以使用C++中的范围for语句

for (declaration : expression)
    statements

要想实现对每个字符的修改需要加上&引用

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

using std::cin;
using std::cout;
using std::string;
using std::endl;

int main() {
    string str("Hello world!!!!!!!");
    decltype(str.size()) punct_cnt = 0;
    for (auto c : str) {
        if (ispunct(c))
            punct_cnt++;
    }
    cout << punct_cnt << " punctuation characters in " << str << endl;
    //利用引用可以实现对str中的字符的修改
    for (auto &c : str) {
        c = toupper(c);
    }
    cout << str << endl;
    return 0;
}
// 7 punctuation characters in Hello world !!!!!!!
// HELLO WORLD !!!!!!!

3.3标准库类型vector

vector是具有相同数据类型的对象的集合,注意是对象的集合不能是引用。

3.3.1定义和初始化vector对象

常用初始化vector对象的方法:

vector<T> v1;
vector<T> v2(v1);   //v2中含有v1的所有元素
vector<T> v2 = v1;  //等价于v2(v1)
vector<T> v3(n,val);    //v3中含有n个重复的元素,每个都是val
vector<T> v4(n);    //v4中包含了n个默认初始化的对象
vector<T> v5{a, b, c, ....};    //列表初始化
vector<T> v5 = {a, b, c, ....};
vector<int> ivec;
vector<int> ivec2(ivec);    //将ivec中的元素拷贝给ivec2
vector<string> svec(ivec2); //错误,对象不同
vector<string> v1{"a", "b","c"};    //列表初始化
vector<string> v2("a", "b", "c");   //错误的列表初始化

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元素,每个都是空
vector<int> v1(10); //10个0元素
vector<int> v2{10}; //1个元素,元素值是10
vector<int> v3(10, 1);  //10个元素,每个都是1
vector<int> v4{10,1};   //两个元素,分别是10,1

()表示用来构造vector对象的,{} 表示用来列表初始化的,把{}里面的值当成是元素初始值的列表处理
如果初始化时采用了{}但是提供的值又不能用来列表初始化,就考虑用这样的值来构造vector对象

vector<string> v5{"hi"};    //列表初始化
vector<string> v6("string");    //错误,不能使用字面值类构造string对象
vector<string> v7{10};  //构造10个空string对象
vector<string> v8{10, "hi"};    //有10个值为"hi"的元素

3.4迭代器介绍

3.4.1使用迭代器

begin成员返回指向第一个元素的迭代器,end成员返回指向容器“尾元素的下一位置”,该迭代器指示的是容器的一个本不存在的尾后元素,没有实际意义,主要是用来标记最后一个位置
迭代器的运算符

语法 功能
*iter 返回迭代器所指向的元素的引用
iter->item 解引用iter并获取名为item的成员,等价于(*iter).mem
++iter 指向下一个元素
--iter 指向上一个元素
iter1 == iter2 判断迭代器是否相等
iter1 != iter2 判断迭代器不等

注意解引用的迭代器必须指向一个确切的元素,不能解引用非法迭代器和尾迭代器
迭代器类型

vector<int>::iterator it;   //it能读写vector<int>的元素
string::iterator it2;
vector<int>::const_iterator it3;    //it3只能读不能写
string::const_iterator it4;

const_iterator 类似于常量指针,对象只能读不能写, iterator 对象可读可写,如果vector\string对象是一个常量只能用const_iteraotr 反之都能用

beign和end运算符
如果对象是常量begin返回const_iterator,反之返回iterator.C++11提供cbegin、cend可直接返回const_iterator

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

结合解引用和成员访问操作
*it通过解引用可以访问迭代器所指对象,如果对象是一个类,可以通过(*it).empty() 来访问,C++提供->运算符将解引用和成员访问结合起来,(*it).empty()等价于it->empty()

3.4.2迭代器运算

语法 描述
iter+n 加上一个整数的结果还是迭代器
iter-n 减去一个整数的结果还是迭代器
iter+=n iter向后移动N个位置
iter-=n iter向前移动N个位置
iter1 - iter2 两个迭代器相减,结果是两个迭代器之间的距离
>,>=,<,<= 比较的是两个迭代器之间的距离

3.5数组

3.5.1定义和初始化内置数组

理解复杂的数组声明

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

默认情况下类型修饰符从右向左依此绑定,比如int *ptrs[10] 从右向左理解先是定义一个大小为10的数组,名字是Ptrs,然后是数组中存放的是指向整型的指针。
对于这种带有括号的,采用从内向外理解比如int (*Parray)[10] = &arr ,括号内显示一个名为Parray的指针,然后在从右向左理解,右边是一个大小为10的数组,所以Parray是一个指向大小为10的数组的指针,再然后是左边int,说明Parray是一个指针,指向一个包含10个元素的整型数组。同样的方法理解int (&arrRef)[10] = arr ,括号内是一个名为arrRef的引用,右边是一个大小为10的数组,所以arrRef是一个大小为10的数组的引用,左边是int,结合起来就是arrRef是一个引用,引用的对象是一个大小为10的整型数组。

3.5.3指针和数组

指针也是迭代器
vector和string的迭代器的运算,数组的指针都能用

int arr[] = {0,1,2,3,4,5,6,7,8,9};
int *p = arr;   //p指向数组元素的首地址
int *e = arr[10];   //e指向数组尾元素的下一个位置的指针
for (int *p = arr; p != e; ++ p)
    cout << *p << endl;

标准库函数中提供begin和end函数
C++11新标准中引用begin函数和end函数来获取数组的首元素指针和尾元素的下一个位置的指针

int arr[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = begin(arr);  //指向arr首元素的指针
int *last = end(arr);   //指向arr尾元素的下一个位置的指针
while(beg != last)
    cout <<*last << endl;

3.5.5与旧代码的接口

混用string对象和C风格字符串
如果程序的某处需要一个C风格的字符串,无法直接用string对象来代替,比如:不能直接用string对象直接初始化指向字符的指针。可以利用c_str()函数完成该功能

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

c_str()函数的返回结果是一个指针,该指针指向一个以空字符结束的字符数组,数组中的数据就是s中存储的内容。

使用数组初始化vector对象

int int_arr = {0,1,2,3,4,5,6,7,8,9};
//ivec被初始化为int_arr中的全部元素
vector<int> ivec(begin(int_arr), end(int_arr));
//ivec初始化为int_arr中int_arr[1], int_arr[2], int_arr[3]
vector<int> ivec(int_arr + 1, int_arr + 4);

posted on 2021-05-26 10:32  翔鸽  阅读(49)  评论(0编辑  收藏  举报