字符串、向量和数组
头文件中不应该使用using
的声明,因为头文件的内容会拷贝到所有引用它的文件中去,若头文件中使用了using
,则每个引用该头文件的文件都会有这个声明。
std::string
std::string
的初始化方式:
#include <string>
using std::string;
string s1;
string s2(s1); // s2是s1的副本
string s2=s2; // s2(s1)等价
string s3="value"; // s3是字面值value的副本,等价s3("value")
string s4(5,'c'); // 等价 s4="ccccc";
使用等号的是拷贝初始化,不用等号的是直接初始化。
使用std::cin
和std::cout
对string
进行读写操作。
读取未知量的string
对象,遇见文件结束标志后停止:
int main(){
string word;
while(cin>>word){
cout<<word<<endl;
}
return 0;
}
使用getline
读取整行,但是不包括结尾的换行符!
int main(){
string line;
while(getline(cin,.line)){
cout<<line<<endl;
}
}
string::size_type
是表示string
大小的数据类型,表达式中出现了string::size()
时,不要使用int
,而是使用auto
自动推导。
string s("12345");
auto len=s.length(); // len是string::size_type类型
字符串运算:
==
:长度和对应字符完全相同>=
、<=
、>
、<
比较第一个相异字符的字典序+
,两个字符串拼接,也可以和字面值进行拼接,s+"value"
;但是字面值不能直接相加
遍历字符串使用for
:
string s("1234567");
for(auto c:s){ // 在这里的c是拷贝的,不是引用!
cout<<c;
}
for(auto &c:s){ // 这里是引用!
cout<<toupper(c); // 原来的也变成了大写
}
也可以按照下表访问:
string s("1234567890");
// 注意使用类型推导
for(decltype(s.size()) index=0;index!=s.size();++index){
s[index]=toupper(s[index]); // 字母改成大写
}
std::vector
vector
对于类型一般是引用的方式,而非拷贝赋值,因此不存在对vector
的引用!
初始化的方法:
vector<T> v1; // 空的vector
vector<T> v2(v1); // v2包含v1的所有副本
vector<T> v3=v1; // 等价于v3(v1)
vector<T> v4(n,val); // v4包含n个val
vector<T> v5{a,b,c}; // v5包含a b c
vector<T> v6={a,b,c}; // v6和v5等价
vector<T> v7(10); // v7包含10个元素,初始值由元素的类型确定
vector<string> v8{10,"hi"}; // 包含10个hi
使用vector::push_back
向末尾添加元素,这种做法是最高效的。
其他的一些比较操作类比于string
。使用size时,要注意指定类型:
vecotr<int>::size_type; // 这是正确的
vector::size_type; // 这是错误的!!!!!!!
下标规则参照string
迭代器
所有的标准库成员都有迭代器,begin()
是第一个元素,end()
是最后一个元素的后一位,实际意义仅仅是结束的标记。空的容器的迭代器的begin()==end()
。
vector<int> v;
auto b=v.begin(), e=v.end(); // 使用自动推导
auto cb=v.cbegin(),ce=v.cend(); // 区别在于不能通过迭代器修改元素
一般规则:
*iter
返回迭代器指向元素的引用iter->mem
与(*item).mem
等价,是访问某元素的mem
成员++iter
指向下一个元素--iter
指向上一个元素iter1==iter2
或者iter1!=iter2
判断是否指向同一个元素。- 不能对
end
进行递增或者解引用操作 - 使用了迭代器的循环体后,不能在循环体中向容器添加元素!!!
vector
和string
支持的运算规则:
iter+n
向后指向的第n
个,或者指向end
ietr-n
向前指向的第n
个,或者是begin
iter-=n
和iter+=n
是对本身的操作>=
>
<=
<
比较的是相对位置
迭代器位置做差,返回的是difference_type
的类型,一般用auto
自动推导:
// 二分查找
auto b = text.begin(), e = text.end();
auto mid = text.begin() + (e - b) / 2;
while(mid != e && *mid != goal) { // goal是目标值
if(goal < *mid) {
e = mid;
} else {
b = mid + 1;
}
mid = b + (e - b) / 2;
}
数组
初始化的方式:
const unsigned sz=3;
int a1[sz]={0,1,2}; // 0 1 2 三个元素
int a2[]={1,2,3}; // 0 1 2 三个元素
int a3[5]={0,1,2,}; // 0 1 2 0 0
string a4[3]={"hello","world"}; // "hello" "world" ""
int a5[2]={0,1,2}; // 错误,初始值过多
默认情况下,数组类型从右向左依次绑定。数组名是地址,指向第一个元素。数组的下表是size_t
类型。数组的指针也是迭代器,满足迭代器的一般运算规则,数组迭代器同时满足vector
的标准。
现代的C++程序应该尽量使用vector
而非数组,使用string
而非C风格的字符串。
两种不同的for
索引多维数组的方式:
constexpr size_t rowCnt = 3, colCnt = 4;
int ia[rowCnt][colCnt];
for(size_t i = 0; i != rowCnt; ++i) {
for(size_t j = 0; j < colCnt; ++j) {
ia[i][j] = i * colCnt + j;
}
}
size_t cnt = 0;
for(auto &row : ia) {
for(auto &col : row) {
col = cnt;
cnt++;
}
}
若使用上述第二种方式遍历,除了最内层的循环外,其他所有循环的控制变量都必须是引用类型!!!!如果不想更改数据,可以添加const
限定!
多维数组的数组名是指针的指针:
int a[3][4]; // 大小为3的数组,每个元素都是含有4个整数的数组
int (*p)[4]=a; // 指向含有4个整数的数组
int *ip[4]; // 整形指针的数组