标准库类型string
1. string对象的初始化
- 当初始值只有一个时,使用拷贝初始化或者直接初始化都行;
- 当有多个初始值时,一般只能使用直接初始化;若非要使用拷贝初始化,则需要显式创建临时变量用于拷贝;
// 默认初始化, s1为空串
string s1;
// 拷贝初始化:使用=号初始化一个变量
string s2 = "value";
string s2 = s1;
string s2 = string(10, 'c'); // string temp(10,'c'); string s2 = temp;
// 直接初始化:使用()号初始化一个变量
string s3("value");
string s3(10,'c'); // 10个c
2. string对象的操作
-
string对象的读取会自动忽略开头的空白,直到下一处空白为止;
#include <iostream> #include <string> using std::string; int main() { string s1, s2; std::cin >> s1 >> s2; std::cout << "s1: " << s1 << std::endl; std::cout << "s2: " << s2 << std::endl; return 0; } // input hello world // output s1:hello s2:world -
getline只要一遇到换行符就结束读取操作并返回结果#include <iostream> #include <string> using std::string; int main() { string line; while (std::getline(std::cin, line)) { std::cout << line << std::endl; } return 0; } -
string::size_type是字符串的长度类型,类似此类型具有与机器无关的特性,是一个无符号类型的值;#include <iostream> #include <string> using std::string; int main() { string line; while (getline(std::cin, line)) { if (line.empty()) { std::cout << "input is empty" << std::endl; } else { string::size_type sz = line.size(); std::cout << "input size is " << sz << std::endl; } } return 0; } -
string对象相加: 得到一个新的string对象
- 字符串字面值与string是不同的类型
- 字符字面值和字符串字面值可以转换为string对象, 但
- 加法运算符的两侧至少有一个必须是string对象!!!
#include <iostream> #include <string> using std::string; int main() { string s1 = "hello", s2 = "world"; string s3 = s1 + "," + s2 + '\n'; // ok string s4 = "hello" + "world"; // not ok string s5 = s1 + "," + "world"; // ok string s6 = "hello" + "," + s2; // not ok return 0; } -
cctype头文件中定义了一组标准库函数用来处理字符- 如果想要改变string对象中的字符,要么把循环变量定义为引用类型,要么使用下标迭代
#include <cctype> #include <iostream> #include <string> using std::cout; using std::endl; using std::string; int main() { string s = "123...Hello, world!"; for (auto c : s) { if (std::isalpha(c)) { cout << c << " 是一个字母 "; if (std::islower(c)) { char c_upper = std::toupper(c); cout << "大写为 " << c_upper << endl; } if (std::isupper(c)) { char c_upper = std::tolower(c); cout << "小写为 " << c_upper << endl; } } if (std::isdigit(c)) { cout << c << " 是一个数字" << endl; } if (std::ispunct(c)) { cout << c << " 是一个标点符号" << endl; } } // 使用范围迭代 for (auto &c : s) { c = std::toupper(c); } cout << "全大写 " << s << endl; // 使用下标迭代 for (decltype(s.size()) i = 0; i != s.size() && !std::isspace(s[i]); i++) { s[i] = std::tolower(s[i]); } cout << "一半小写 " << s << endl; return 0; }
标准库类型vector
Vector: 相同类型对象的集合
1. vector对象的初始化
// 默认初始化
vector<int> v1; // 空vector,元素类型为int
// 拷贝初始化
vector<int> v2(v1); // v2包含v1中所有元素的副本
vector<int> v2 = v1; // 同上
// 直接初始化
vector<int> v3(10, 1); // v3包含10个1
vector<int> v4(10); // v4包含10个0, 10个元素执行了默认值初始化
// 列表初始化
vector<int> v5{1,3,5}; // v5包含1,3,5
vector<int> v5 = {1,3,5}; // 同上
// 使用了花括号,但无法执行列表初始化,将尝试使用直接初始化
vector<string> v6{10}; //v6包含10个空字符串
vector<string> v7{10,"hi"}; // v7包含10个"hi"
2. vector对象的操作
与其他语言(c、java、golang)不同,定义vector对象时设定其大小,性能可能更差
#include <iostream>
#include <vector>
using std::vector;
int main() {
vector<int> ivec;
if (ivec.empty()) {
for (int i = 0; i < 10; ++i) {
ivec.push_back(i);
}
}
for (auto &v : ivec) {
v *= v;
}
for (vector<int>::size_type ix = 0; ix != ivec.size(); ++ix) {
std::cout << ivec[ix] << std::endl;
}
return 0;
}
迭代器iterator
-
和指针类似,可以通过解引用来获取迭代器指示的值;
-
如果vector对象或者string对象是常量,则只能使用
const_iterator;vector<int>::iterator it; //it可读写vector<int>的元素 string::iterator strit; // strit可读写string中的字符 vector<int>::const_iterator cit; // cit只能读vector<int>的元素,不能写 string::const_iterator strcit; // strcit只能读string中字符,不能写 -
函数
begin()和end()返回vector<T>::iterator类型, 相应的cbegin()和cend()则返回vector<T>::const_iterator类型; -
但凡使用了迭代器的循环体,则不能向迭代器所属容器添加元素;
-
迭代器支持
+、-、>=、<=等算数运算操作。#include <iostream> #include <string> using std::string; int main() { const string text = "a b c d e f g h i j k l m n o"; const char target = 'h'; string::const_iterator beg = text.cbegin(), end = text.cend(); string::difference_type half_len = (end - beg) / 2; string::const_iterator mid = beg + half_len; // 二分查找 while (mid != end && *mid != target) { if (target < *mid) { end = mid; } else { beg = mid + 1; } mid = beg + (end - beg) / 2; } std::cout << *mid << std::endl; // 如果mid==end则没有找到目标元素 std::cout << (mid == end) << std::endl; return 0; }
内置类型array
array: 存放相同类型对象的容器,大小确定,运行时性能较好,灵活性欠佳。
当不清楚元素确切个数时,使用vector
1. array对象的初始化
-
数组的维度必须是常量表达式
-
数组的元素被默认初始化
-
数组的元素应为对象,不存在引用的数组
const unsigned n = 3; int arr1[n] = {0,1,2}; // 含三个元素的数组 int arr2[] = {0,1,2}; // 同上 int arr3[5] = {0,1,2}; // 含5个元素的数组, 0,1,2,0,0 string arr4[3] = {"hello", "world"}; // 含三个字符串元素, "hello"、"world","" int *ptrarr[10]; // 含有10个int*的数组 int &refarr[10]; // not ok. 不存在引用的数组 int (*arrptr)[3] = &arr1; // 数组指针,arrptr指向一个数组, 该数组含有3个int int (&arrref)[3] = arr1; // 数组引用,arrref是数组arr1的引用,是arr1的别名 -
使用字符串字面值初始化一个数组时,含有一个空字符
char arr5[] = "c++"; char arr5[] = {'c','+','+','\0'}; // 同上 -
数组不允许拷贝和赋值
int arr6[] = {0,1,2}; int arr7[] = arr6; // not ok. 不允许使用一个数组初始化另一个数组 arr7 = arr6; // not ok. 不能把一个数组直接赋值给另一个数组
2. array对象的操作
指针和数组
-
通常用到数组名字的地方,编译器会自动将其替换为指向数组首元素的指针;
string nums[] = {"one","two","three"}; string *p = nums; // 等同于 string *p = &nums[0]; auto pnums(nums); // 等同于指针拷贝 auto pnums(&nums[0]); // pnums是一个string*,指向nums的第一个元素 -
当使用
decltype关键字时,数组不会被转换为首元素地址;decltype(nums) nums2 = {"four","five","six"}; -
使用标准库函数
begin()和end()可以获得数组的首元素指针和尾后指针;string *beg = begin(nums); string *end = end(nums); -
对数组执行下标运算等价于对指向数组元素的指针执行下标运算;
标准库类型(如string、vector)使用的下标必须是无符号类型,内置类型(如array)的下标无此要求。
string third_num = nums[2]; // 等价于 string *numsprt = nums; third_num = *(numsprt + 2); string *second = &nums[1]; string third = second[1]; // 等价于 *(second+1) string first = second[-1]; // 等价于 *(second-1)
操作示例:
#include <cstddef>
#include <iostream>
#include <iterator>
#include <string>
using std::string;
int main() {
const unsigned n = 3;
string nums[n] = {"one", "two", "three"};
string *beg = std::begin(nums);
string *end = std::end(nums);
// 移动首元素指针
string *second_num = nums + 1;
std::cout << "nums第二个元素: " << *second_num << std::endl;
// 指针相减,ptrdiff_t是带符号类型
std::ptrdiff_t sz = end - beg;
std::cout << "nums含有" << sz << "个元素" << std::endl;
// 指针遍历
while (beg != end) {
std::cout << *beg << std::endl;
++beg;
}
// 数组下标操作等价于指针操作
string third_num = nums[2]; // 等价于
string *numsprt = nums;
third_num = *(numsprt + 2);
std::cout << "nums第三个元素: " << third_num << std::endl;
string *second = &nums[1];
string third = second[1]; // 等价于 *(second+1)
string first = second[-1]; // 等价于 *(second-1)
std::cout << "first: " + first << std::endl;
std::cout << "second: " + *second << std::endl;
std::cout << "third: " + third << std::endl;
return 0;
}
c风格字符串
c风格字符串本质是char[],数组会转换为指向首元素的指针,因此
- c风格字符串函数的参数实质都是指针,且
- 指针参数指向的数组必须以空字符结尾
strlen(p); // p的长度
strcmp(p1,p2); // 比较
strcat(p1,p2); // p2附加到p1,返回p1
strcpy(p1, p2); // 将p2拷贝给p1,返回p1
#include <cstring>
#include <iostream>
#include <string>
int main(int argc, const char **argv) {
char arr1[] = {'h', 'e', 'l', 'l', 'o', '\0'};
std::cout << "len(arr)=" << strlen(arr1) << std::endl;
char arr2[] = " world";
// arr2拼接到arr1
std::strcat(arr1, arr2);
// arr1 拷贝给 arr3
char arr3[strlen(arr1)];
std::strcpy(arr3, arr1);
// 比较
int cmp = std::strcmp(arr1, arr3);
std::cout << "cmp: " << cmp << std::endl;
if (cmp == 0) {
std::cout << "arr1 == arr3" << std::endl;
} else if (cmp > 0) {
std::cout << "arr1 > arr3" << std::endl;
} else {
std::cout << "arr1 < arr3" << std::endl;
}
return 0;
}
-
允许以空字符结尾的字符数组来初始化string对象或为string对象赋值
-
允许string对象加法运算中其中一个是以空字符结尾的字符数组
-
反过来,如果想要用string对象来初始化一个c风格字符串,则必须使用
c_str()成员函数;char cstr[] = {'h', 'e', 'l', 'l', 'o', '\0'}; string str = cstr; string addstr = cstr + str; const char *c_addstr = addstr.c_str();
多维数组
// 多维数组内层嵌套的花括号并非必需
int aa[3][4] = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11},
}; // 等价于
int aa[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
int aa[3][4] = {{0}, {4}, {8}}; // 显式初始化每行首元素
int aa[3][4] = {0, 4, 8, 12}; // 显式初始化第一行
操作示例:
#include <iostream>
#include <iterator>
int main() {
// 三行四列
int aa[3][4] = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11},
};
// p指向类型为int[4]
int(*p)[4] = aa; // p指向aa的首元素
p = &aa[1]; // p指向aa的第二个元素
// 多维数组遍历
// 范围遍历——外层for需要使用引用类型,否则row为首元素指针,内层遍历指针是不允许的
for (auto &row : aa) {
for (auto &col : row) {
std::cout << col << " ";
}
std::cout << std::endl;
}
// 数组指针遍历
for (auto p = std::begin(aa); p != std::end(aa); ++p) {
for (auto q = std::begin(*p); q != std::end(*p); ++q) {
std::cout << *q << " ";
}
std::cout << std::endl;
}
return 0;
}
参考书籍:《C++ Primer》第5版