C++ primer 5学习第二天

命名空间的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函数来确定数组上下界

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值