从0.1开始的C++ Note 03

该笔记包括stringvector、迭代器和数组。

标准库类型string

#include <string>
using std::string;

定义和初始化string对象

string s1;    // 默认初始化,s1是一个空字符串
string s2(s1)    // s2是s1的副本
string s2 =s1;    // 等价于s2(s1),s2是s1的副本
string s3("hiya");    // s3是字面值"hiya"的副本,除了字面值后面的那个空字符外
string s3 = "hiya";    // 等价于s3("hiya"),s3是该字符串字面值的副本
string s4(10,'c');    // s4的内容是cccccccccc

如果使用等号初始化一个变量,实际执行的是拷贝初始化,编译器把等号右边的初始值拷贝到新创建的对象中去;反之,如果不使用等号,则执行的是直接初始化。

string对象上的操作

代码操作
os<<s将s写到输出流os当中,返回os
is>>s从is读取字符串赋给s,字符串以空白分隔,返回is
getline(is, s)从is中读取一行赋给s,返回is
s.empty()s为空返回ture,否则返回is
s.size()返回s中字符的个数
s[n]返回s中第n个字符的引用,位置从0计起
s1+s2返回s1和s2连接后的结果
s1=s2用s2的副本代替s1中原来的字符
s1==s2 & s1 !=s2判断字符串是否完全一样,对大小写敏感
<,<=,>,>=利用字符在字典中的顺序进行比较,且对字母的大小写敏感

读写string对象

int main(){
    string s;    // 空字符串
    cin >> s;    // 忽略开头的空白,从第一个真正的字符开始将string对象读入s,遇到空白停止
    cout << s << endl;    //输出s
    return 0;
};

使用getline读取一整行

int main(){
    string line;
    //每次读取一整行,直至到达文件末尾
    while (getline(cin, line))
        //跳过空行
        if (!line.empty())
            //每次读取一整行,输出其中超过80个字符的行
            if (line.size() > 80)
                cout << line << endl;
    return 0;
};

string::size_type类型
比较string对象

  1. 如果两个string对象的长度不同,而且较短string对象的每个字符都与较长字符string对象对应位置上的字符相同,就说较短string对象小于较长string对象。
  2. 如果两个string对象在某些对应的位置上不一致,则string对象比较结果其实是string对象中第一对相异字符比较的结果。
string str = "Hello";
string phrase = "Hello World";
string slang = "Hiya";

由规则可知:slang>phrase>str。

处理string对象中的字符

cctype头文件

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

使用范围for语句处理每个字符

for (declaration : expression)
    statement

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

string s("Hello World!!!");
//decltype返回s.size的类型
deltype(s.size()) punct_cnt = 0;
//统计s中标点符号的数量
for (auto c : s)
    if (ispunct(c))
        ++puct_cnt;
cout << punct_cnt << " punctuation characters in " << s <<endl;

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

string s("Hello World!!!");
//转换成大写形式
for (auto &c : s)    //c是引用!
    c = toupper(c);    //c是一个引用,因此赋值语句将改变s中字符的值
cout << s << endl;

使用下标进行迭代

//依次处理s中的字符直至我们处理完全部字符或者遇到一个空白
for (deltype(s.size()) index = 0;
     // index必须大于等于0,且小于s.size()
     // &&运算符仅会在左侧对象为真时才会检查右侧运算情况
     index != s.size() && !isspace(s[index]); ++index)
        s[index] = toupper(s[index]);

标准库类型vector

#include <vector>
using std::vector;

定义和初始化vector对象

代码操作
vector\ v1v1是一个空vector,它潜在的元素是T类型的,执行默认初始化
vector\ v2(v1)v2中包含有v1所有元素的副本
vector\ v2=v1等价于v2(v1)
vector\ v3(n,val)v3包含了n个重复的元素,每个元素的值都是val
vector\ v4(n)v4包含了n个重复地执行了值初始化的对象
vector\ v5{a,b,c…}v5包含了初始值个数的元素,每个元素被赋予了相应的初始值
vector\ v5={a,b,c…}等价于v5{a,b,c…}
//默认初始化
vector<int> ivec;
vector<int> ivec2(ivec);
vector<int> ivec3 = ivec;

//列表初始化
vector<string> v1{"a", "an", "the"};
//创建指定数量的元素
vector<int> ivec(10, -1);
//值初始化
vector<int> ivec(10);    //10个元素,每个都初始化为0
vector<string> svec(10);    //10个元素,每个都是空string对象

vector对象中添加元素

使用push_back向vector中添加元素:

vector<int> v2;    // 空vector对象
for (int  i = 0; i != 100; ++i)
    v2.push_pack(i);    // 依次把整数值放到v2尾端

Warning from Primer:范围for语句体内不应改变其所遍历序列的大小。

其他vector操作

代码操作
v.empty()如果v不含有任何元素,返回真;否则返回假
v.size()返回v中元素的个数
v.push_back(t)向v的尾端添加一个值为t的元素
v[n]返回v中第n个位置上元素的引用
v1=v2用v2中元素的拷贝替换v1中的元素
v1={a, b, c…}在列表中元素的拷贝替换v1中的元素
v1==v2, v1!=v2v1和v2是否相等
<,<=,>,>=以字典顺序进行比较

范围for语句访问vector对象中元素

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;

计算vector内对象的索引

vector<unsigned> scores(11, 0);
unsigned grade;
while (cin >> grade) {
    if (grade <= 100)
        ++scores[grade/10];
}

迭代器介绍

使用迭代器

beginend成员

// 由编译器决定b和e的类型;
// b表示v的第一个元素,e表示v尾元素的下一个位置
auto b = v.begin(), e = v.end();    //b和e的类型相同

Note:如果容器为空,则beginend返回的是同一个迭代器,都是尾后迭代器。
迭代器运算符

代码操作
*iter返回迭代器iter所指元素的引用
iter->mem解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem
++iter令iter指示容器中的下一个元素
–iter令iter指示容器中的上一个元素
iter1 == iter2判断两个迭代器是否相等(不相等),如果两个迭代器指示的是同一个元素或者它们是同一个容器的尾后迭代器,则相等;反之不相等

举例:

string s("some string");
if (s.begin() != s.end()) {    // 确保s非空
    auto it = s.begin();    //it表示s的第一个字符
    *it = toupper(*it);    //将当前字符改成大写形式
}

// 将字符串第一个单词全部改为大写形式
// 依次处理s的字符直至处理完或遇到空白
for (auto it = s.begin(); it != s.end && !isspace(*it); ++it)
    *it = toupper(*it);    //将当前字符改成大写形式

迭代器类型

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

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

cbegincend
不论vector(string)对象本身是否为常量,返回值都是const_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

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

(*it).empty()    //解引用it,然后调用结果对象的empty成员

C++定义了箭头运算符(->)。箭头运算符将解引用和成员访问两个操作结合在一起,也就是说,it->mem(*it).mem表达的意思相同。

// 依次输出text的每一行,直至遇到第一个空白为止
for (auto it = text.cbegin();
    it != text.cend() && !it->empty(); ++it)
    cout << *it << endl;

迭代器运算

使用迭代器运算进行二分查找

// text必须是有序的
// beg和end表示我们的搜索范围
auto beg = text.begin(), end = text.end();
auto mid = text.begin() + text.size()/2;    //初始状态下的中间点
//当还有元素尚未检查并我们还没有找到sought时执行循环
while (mid != end && *mid != sought) {
    if (sought < *mid)
        end = mid;
    else
        beg = mid+1;
    mid = beg + (end - beg)/2;
}

数组

定义和初始化

unsigned cnt = 42;    //不是常量表达式
constexpr unsigned sz = 42;    //常量表达式
int arr[10];    //含10个整数的数组
int *parr[sz];    //含有42个整型指针的数组
string bad[cnt];    //错误:cnt不是常量表达式
string strs[get_size()];    //当get_size是constexpr时正确;否则错误

显式初始化数组元素

const unsigned sz = 3;
int ia1[sz] = {0, 1, 2};    //含有3个元素的数组,
int a2[] = {0, 1, 2};    //维度时3的数组
int a3[5] = {0, 1, 2};    //等价于a3[] = {0, 1, 2, 0, 0}
string a4[3] = {"hi", "bye"};    //等价于a4[] = {"hi", "bye", ""}
int a5[2] = {0, 1, 2};    //错误:初始值过多

字符数组的特殊性:可以用字符串字面值进行初始化,但结尾处的空字符也会被拷贝进字符数组。

char a1[] = {'C', '+', '+'};    //列表初始化,没有空字符
char a2[] = {'C', '+', '+', '\0'};    //列表初始化,含有显式的空字符
char a3[] = "C++";    //自动添加表示字符串结尾的空字符
const char a4[6] = "Daniel";    //错误:没有空间可存放空字符

不允许拷贝和赋值:不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。

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

理解复杂的数组声明

int *ptrs[10];    //ptrs是含有10个整型指针的数组
int (*Parray)[10] = &arr;    //Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr;    //arrRef引用一个含有10个整数的数字
int *(&arry)[10] = ptrs;    //arry是数组的引用,该数组含有10个指针

下标访问和遍历

unsigned score[11] = {};
unsigned grade;
while (cin >> grade){
    if (grade <= 100)
        ++scores[grade/10];
}

for (auto i : scores)
    cout << i << " ";
cout <<endl;

指针和数组

string nums[] = {"one", "two", "three"};
string *p = &nums[0];    //p指向nums的第一个元素
//数组特性,在很多用到数组名字的地方,编译器会自动将其替换为一个指向数组首元素的指针
string *p2 = nums;    //等价于p2 = &nums[0]

指针也是迭代器

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;    //输出arr的元素

标准库函数beginend

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

指向数组的指针可以执行与迭代器相似的运算

constexpr size_t sz = 5;
int arr[sz] = {1,2,3,4,5};
int *ip = arr;
int *p2 = ip + 4;    //ip2指向arr的尾元素arr[4]

解引用和指针运算的交互

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

下标和指针

int ia[] = {0,2,4,6,8};
int i = ia[2];
int *p = ia;
i = *(p + 2);    //等价于i = ia[2]

int *p = &ia[2];    //p指向索引为2的元素
int j = p[1];    //p[1]等价于*(p + 1)
int k = p[-2];    //p[-2]是ia[0]表示的那个元素

多维数组

多维数组实际上是数组的数组。

int ia[3][4];
int arr[10][10][10] = 0;

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}};
//显示地初始化第1行,其他元素执行值初始化
int ix[3][4] = {0, 3, 6, 9};

多维数组的下标引用

ia[2][3] = arr[0][0][0];
int (&row)[4] = ia[1];    //把row绑定到ia的第二个4元素数组上

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

size_t cnt = 0;
for (auto &row : ia)    //对于外层数组的每一个元素
    for (auto &col : row) {    //对于内层数组的每一个元素
    col = cnt;
        ++cnt;
    }

指针和多维数组

int ia[3][4];    //大小为3的数组,每个元素是含有4个整数的数组
int (*p)[4] = ia;    //p指向含有4个整数的数组
p = &ia[2];    //p指向ia的尾元素

指针遍历

//p指向ia的第一个数组
for (auto p = begin(ia); p != end(ia); ++p) {
    //q指向内层数组的首元素
    for (auto q = begin(*p); q != end(*p); ++q)
        cout << *q << ' ';
cout << endl;
}

作者: 公子小白

SOS团团员,非外星人、未来人、超能力者。。。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注