可以说string和vector是C++标准库中最重要的两种类型,string支持可变长字符串,而vector表示可变长的集合。
string
头文件:
定义在命名空间 std 中,using std::string;
string s1; // 默认初始化,s1是一个空串
string s2(s1); // s2是s1的副本
string s3 = s1; // s3是s1的副本
string s4("value"); // s4是字面值"value"的副本,除了字面值最后的那个空字符外
string s5 = "value"; // 等价于上面的括号初始化
string s6(n, 'c'); // 把s6初始化为由连续n个字符c组成的串
这里需要区分直接初始化和拷贝初始化(后面学了类相关的知识就会知道,这两种初始化调用分别调用的是构造函数和拷贝控制的某种构造函数)。
使用等号(=)初始化一个变量,实际上执行的就是拷贝初始化(copy initialization),编译器把等号右侧的初始值拷贝到新创建的对象中去。反之,不使用等号,则执行的就是直接初始化(direct initialization)。
string的操作
os << s 将s写到输出流os中,返回os
is >> s 从is中读取字符串赋给s,字符串以空白分隔,返回is
getline(is, s) 从is中读取一行赋给s,返回is
s[n] 返回s中第n个字符的引用,位置n从0计起
s1 + s2 返回s1和s2连接后的结果
// 读取未知数量的string对象
int main()
{
string word;
while (cin >> word)
cout << word << endl;
return 0;
}
string的size()函数返回类型为string::size_type,它是一个无符号类型的值,而且能足够存放下任何string对象的大小。
string对象与字符字面值或者字符串字面值相加时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string对象:
int main()
{
string str1("hell");
string str2 = str1 + 'o' + " world";
cout << str2 << endl;
return 0;
}
如何处理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时空白时为真(即c是空格、横向制表符、纵向制表符、回车、换行、进纸符)
isupper(c) 当c是大写字母时为真
isxdigit(c) 当c是十六进制数字时为真
tolower(c) 如果c是大写字母,输出对应的小写字母;否则原样输出c
toupper(c) 如果c是小写字母,输出对应的大写字母;否则原样输出c
string str3("abcdefgh0123");
for (auto c : str3) {
if (isxdigit(c)) {
cout << "0x" << c << endl;
} else {
cout << c << endl;
}
}
vector
也被称为容器(container),头文件
vector是一个类模板(class template)。模板本身不是类或函数,可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为实例化(instantiation),当使用模板时,需要指出编译器应该把类或函数实例化成何种类型。
vector的使用
vector
vector
vector
定义和初始化
vector
vector
vector
vector
vector
vector
vector
使用列表初始化时,注意:
vector
vector
vector
vector
用圆括号:提供的值用来构造(construct)vector对象
用花括号:列表初始化该vector对象,也就是说,初始化过程会尽可能地把花括号内的值当成是元素初始值的列表来处理,只有在无法执行列表初始化时才会考虑其他初始化方式。
vector
vector
上面两条语句尽管使用了花括号,但是花括号中的值(10)与元素类型(string)不匹配,不能作为元素的初始值,编译器会尝试使用构造函数去初始化对象。
vector
使用push_back向vector对象中添加元素
vector
for (int i = 0; i != 10; ++i) {
ivec.push_back(i);
}
迭代器(iterator)
提供迭代器的类型,拥有begin和end成员函数,begin返回指向第一个元素(或第一个字符)的迭代器;end返回容器(或string对象)“尾元素的下一位置”的迭代器。
auto b = ivec.begin(), e = ivec.end();
如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器。
拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器的类型:
vector
string::iterator iter2;
vector
vector和string迭代器支持的运算
iter + n iter - n iter += n iter -= n
iter1 - iter2 两个迭代器相减的结果是它们之间的距离,参与元素的两个迭代器必须指向的是同一个容器中的元素或尾元素的下一位置
>、>=、<、<=
注意:两个迭代器不能相加!!!(没有意义)
数组
在C++代码中,尽可能地使用vector,而不使用数组。
数组的声明:a[d],其中a是数组的名字,d是数组的维度。维度说明了数组中元素的个数,因此必须大于0。数组中元素的个数也属于数组类型的一部分,编译的时候维度必须是已知的,因此,维度必须是一个常量表达式:
unsigned cnt = 10;
constexpr unsigned sz = 10;
int arr[10]; // 正确
int *parr[sz]; // 含有10个整型指针的数组
string err[cnt]; // 错误:cnt不是常量表达式
string strs[GetSize()]; // 当GetSize()是constexpr时正确,否则错误
数组下标的类型为size_t,size_t是一种机器相关的无符号类型,被设计得足够大以便能表示内存中任意对象的大小,在头文件
在很多用到数组名字的地方,编译器都会自动地将其替换为一个指向数组首元素的指针:
string nums[] = {"a", "b", "c"};
string *p = &nums[0]; // 指向nums的第一个元素
string *ps = nums; // 等价
在一些情况下,数组的操作实际上是指针的操作,所以当使用数组作为一个auto变量的初始值时,推断得到的类型是指针而非数组:
int ia[] = {0,1,2,3,4,5};
auto ia2(ia); // ia2是一个整型指针,指向ia的第一个元素
当使用decltype关键字时上述转换不会发生:
decltype(ia) ia3 = {0,1,2,3,4,5};
ia3[4] = 10;
标准库函数begin和end,与容器中的两个同名成员功能类似,作用于数组。
C风格字符串
它不是一种类型,而是为了表达和使用字符串而形成的一种约定俗成的写法。按此习惯书写的字符串存放在字符数组中并以空字符结束。
strlen(p) 返回p的长度,空字符不计算在内
strcmp(p1, p2) 比较p1和p2,如果p1==p2,返回0,如果p1>p2,返回正值,否则返回一个负值
strcat(p1, p2) 将p2附加到p1之后,返回p1
strcpy(p1, p2) 将p2拷贝给p1,返回p1
string对象转换为C风格字符串:str.c_str();
本来想把《STL源码剖析》中的vector实现写出来,但是内容就太多了,所以现阶段就只写C++的基础知识,后面再单独写其他深入的内容。