命名空间的using声明
using namespace xxx
通过这个声明我们就可以直接使用名字空间里面的名字
注意:头文件不应该包含using声明
String类
初始化方式很多
string s1(n, 'c') // 初始化n个c字符的字符串
字符串的初始化分直接初始化和拷贝初始化
使用=号就是拷贝初始化
而直接string s1(“dsad”) 则是直接初始化
s1是字面值"dsad"的副本
常用的string对象操作
getline(is, s) 从is读取一行赋值给s
s.empty()如果字符串为空则返回True
s.size()返回字符串s的字符个数
s[n] 返回s中第n个字符的引用
读取未知数量的字符串
我们可以使用while循环来检测是否有流输入到我们的字符串对象
string word;
while(cin>>word){
cout<<word<<endl;
}
return 0;
size_type类型
String对象的size函数返回的是一个size_type类型值,它是一个无符号类型值,并且能足够存放下任何string对象的大小
我们不要用int来指定,直接auto 变量就行
string s1("dasdasdasdas");
auto a1 = s1.size();
cout<<a1<<endl;
string对象的比较操作
1.当两个string对象的长度不同,但较短的string对象上的每个字符都对应较长的string对象字符,则较短string对象小于较长的string对象
2.如果两个string对象在对应的位置不一致,则是比较第一对相异字符比较的结果
string s1 = "abc";
string s2 = "abd";
if (s2>s1){
cout<<"Yes!";
}
输出结果:Yes!
string对象相加操作
相加操作就是让字符串进行拼接
注意:字符串字面值与string是不同类型
string类型的输入操作符忽略所有空白字符,直至再次遇到空白字符则读取终止
getline则不忽略开头的空白字符,读取直至遇到换行符
处理string对象中的字符
cctype头文件中定义了相关处理字符的函数
处理字符串的每个字符我们可以使用for范围语句进行遍历
string s1("zzk nb!");
for(auto &c:s1){
c = toupper(c);
cout<<c;
}
输出结果为:ZZK NB!
我们这里绑定了一个引用,因此s1本身字符串也都转成大写
如果只想处理部分字符,我们使用下标运算符都过索引就能单独对字符串的字符做处理
注意要保证下标大于0,小于字符串的size()返回值
跟数组越界是一个道理
标准库类型Vector
vector是表示对象的集合,类似一种容器
vector实质上是一种类模板
模板可以视为编译器生成类或函数编写的一份说明
编译器根据模板创建类或函数的过程称为实例化
对于类模板,我们需要提供一些额外信息来指定模板实例化成什么类
vector <int> ivec{1, 2, 3, 4, 5};
for(auto c:ivec){
cout<<c<<endl;
}
这里我们初始化了一个存放int类型元素的vector,用花括号进行初始化
另外我们还可以vector a(n, val) 这样就初始化了n个val元素的vector
需要注意的是vector容纳的是对象,而引用不是对象,所以vector不能包含引用,但是vector本身也是一种对象,所以我们可以建立vector的vector
最常见的还是默认初始化,等到有需要的时候再在vector后面继续补充元素
注意初始化时候花括号和圆括号的区别!
vector <int> ivec(10); // 圆括号表示有n个元素
vector <int> ivec2{10};// 花括号里面的代表初始值
如果初始化的时候没有给定vector长度,那它的size就是0
另外还有个特殊情况
vector <string> ivec2{10};// 花括号里面的代表初始值
cout<<ivec2.size();
for(auto c:ivec2){
cout<<c<<endl;
}
此时size输出是10,但是vector里面元素全是空白字符
添加元素
我们可以使用vector类型自带的push_back函数来给容器添加元素
vector <int> ivec; // 圆括号表示有n个元素
for(int i=0;i<10;i++){
ivec.push_back(i);
}
for(auto c:ivec){
cout<<c<<endl;
}
输出结果:
0
1
2
3
4
5
6
7
8
9
注意的是C++里面对vector设定是动态的,可能人们以往思维是给容器指定元素个数运行可以更加高效,但是在C++这里并不是这样的,我们更推荐是默认初始化一个空Vector,再在后面添加对象
例题:从cin读入一组整数并存放
vector <int> ivec;
int i;
while(cin>>i){
ivec.push_back(i);
}
for(auto c:ivec){
cout<<c<<endl;
}
size函数,返回元素个数
empty 当容器内没有元素的时候,返回真
另外要想使用size_type,需指定它是哪种类型定义的
vector<int<::size_type
注意!我们可以通过下标访问到容器内的元素,但不能通过下标进行元素的添加
迭代器
所有标准库容器都可以使用迭代器
迭代器内含有begin和end成员
begin返回的是首元素的位置,end返回的是尾元素的下一个位置,即这个位置是不存在的
一般来说我们不关心迭代器的类型,直接使用auto定义即可
常见的迭代器运算符
*iter 返回迭代器iter所指的引用
iter->mem 解引用iter并获取mem成员
++iter 令iter指示容器中下一个 元素
== != 判断两个迭代器是否相等
例子:获取首迭代器并逐步移动,让字符串的每个字符都变为大写
string s("i love n");
for(auto i=s.begin();i != s.end();i++){
*i = toupper(*i);
}
cout<<s<<endl;
迭代器类型
一般来说我们不知道也无须找到迭代器的类型,常用的是auto
但标准库类型使iterator 和const_iterator来表示迭代器的类型
vector<int>:: iterator it;
vector<int>:: const_iterator it2;
begin 和 end运算符中,如果对象是常量则返回的是const_iterator,否则返回的是iterator类型
如果你一定需要常量类型则可以使用cbegin cend
解引用和访问互相结合
如果这两个操作要同时进行
那解引用那里一定要加一对圆括号
(*it).expmty()
这样会优先进行解引用,再判断里面元素是否为空
不加圆括号的话会先执行it.exmpty()这段语句
为了简化操作我们直接使用箭头也可以替代
it->empty();
等效于上面的那个语句
例子:二分查找
// 二分查找
vector<int> ivec{1, 9, 20, 23, 45, 87, 74};
auto beg = ivec.begin();
auto end = ivec.end();
auto mid = ivec.begin() + (end - beg)/2;
int sought = 20;
while(mid!=end &&*mid!=sought){
if(sought < *mid)
end = mid;
else
{
beg = mid;
}
mid = beg + (end-beg)/2;
}
cout<<*mid<<endl;
数组
数组与vector类似,但是数组的容量是确定的,不能随意增删元素,如果在不确定元素的个数,最好使用vector
初始化
我们可以不指定维度对数组进行初始化
int a2[] = {0, 1, 2, 3, 4};
int a1[5] = {1, 2, 3, 4};
字符数组初始化
我们可以直接指定字符进行初始化
当然我们也可以直接传入一个字符串进行初始化,但不要忘记字符串最后的\0也会传入数组中
char a1[] = {"zzk"};
数组不允许直接拷贝和赋值
理解复杂的数组声明
int *ptrs[10]; // ptrs是含有10个整型指针的数组
int (*parray)[10] = &arr; // Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr; // arrRef引用一个含有10个整数的数组
遇到复杂的数组声明最好是从内向外理解
下面数组声明是正确的
int txt_size(){
int i = 233;
return i;
}
int ia[txt_size()];
访问数组元素
通常使用下标访问,该下标被设置为size_t类型
指针和数组
当我们指定一个指针并用数组名进行赋值,该指针指向的是数组首元素
相当于
int ia[3] = {1, 2, 3};
int *p = ia;
int *p2 = &ia[0];
数组指针也是迭代器,我们可以利用+号加上for循环去遍历数组
标准库中的begin和end
本质上与vector的begin和end方法没区别
但是数组毕竟不是vector内部的,因此标准库函数提供了同名的函数达到相同的作用
即获取首元素指针和尾元素的下一个指针位置
int ia[3] = {1, 2, 3};
int *beg = begin(ia);
int *last = end(ia);
for(auto i=beg;beg<last;beg++){
cout<<*beg<<' ';
}
输出结果:
1 2 3
标准库类型vector和string的下标运算都需要是unsigned无符号类型
而内置类型的下标运算符号则没有这个要求
多维数组
多维数组实质上就是数组的数组
第一个维度称为行 第二个维度称为列
如果想要遍历多维数组,则对应写多个for范围循环嵌套即可
注意使用多层for循环时,除了最内层的循环可以不使用引用,其他几层循环都必须使用引用
for(const auto &row:ia)
for(auto col:row){
.....
}
跟一维数组一样,多维数组名实质上是第一个内层数组的指针
int ia[3][2] ={1, 2, 3, 4, 5, 7};
for(auto p = ia;p!=ia+3;++p){
for(auto i = *p;i!=*p+2;++i){
cout<<*i<<endl;
}
}
这样指定容易出错,最好还是使用begin和end函数来确定数组上下界