正文之前
今天帮学妹选了一天的电脑配件,然后从中领悟:坐看狗东黑我钱,任他涨价我不动!!最后果断的用学妹的钱冲了个Plus,然后领了一堆券,最后学妹还省了30块,另外昨晚找到了一个买酷睿i7 7700k 送主板的,又省了250,然后各种大小活动都去看了个遍,总算是学会了怎么省钱,特此还要多谢我华科电信的一位大佬--“大王”巍的指点,不然我肯定被狗东坑到怀疑人生。看配置的直接翻到正文之后看!
正文
1、 命名空间using声明
我们自己平时用的时候是:
using namespace std;
老实说我也就会这一句,所以其他的基本漠不关心,但是既然书里讲了我也就写出来了。一般的格式如下:
using std::cin;
有了上面这句,以后写cin就只是cin了。不需要std::cin了。不过也仅限于此,cout还是没法直接用,不过这样的好处就是可以单独的引用,不会造成浪费(Maybe)?
另外头文件不应该包含着using声明,因为头文件的内容会被拷贝到所有引用他的文件中取去,如果头文件内有这个using声明,那么每一个使用了该文件的文件就会有这个声明,对于某些程序来说,由于不经意间包含了一些名字,反而可能会产生始料未及的名字冲突。
2、 标准库 string的基本操作
首先,要使用string这个标准库就要声明下头文件
#include <string>
using std::string
- string的初始化
有以下几种种方式
string s1; //s1:empty
string s2=s1; //s2: empty
string s3="string"; //s3:string\0
string s4(10,'c') //s4:cccccccccc
string s5("string"); //s5:string\0
几种方式的过程和效果我都写出来了,就不多赘述了。
- string对象的上的操作
操作形式 | 效果 |
---|---|
cout<<s | 标准输出流。输出整个字符串 |
cin>>s | 从cin标准输入流中一次读取,直到遇到空白符号停下 |
getline(cin,s) | 从cin标准输入流中一次读取一行,不到换行符不停止读取,可以读空白 |
s.empty | 判断是否为空,为空则返回true(bool) |
s.size() | 直接读取字符串的字符个数,包括’\0‘ |
s[n] | 直接读取s这个字符串中第n个字符,从0开始计算 |
s1+s2 | 连接两个字符串 |
s1=s2 | 拷贝赋值 |
s1==s2 | 判断两个字符串是否相等,相等则返回true(bool) |
s1!=s2 | 判断两个字符串是否不等,不等则返回true(bool) |
<,>,<=,>= | 关系运算符,进行字符串的大小比较,后面说 |
- 下面详细说明各个操作:
读写操作,读取的时候自动忽略开头的空白,遇到第一个非空字符开始读取,之后遇到第一个空白字符就停止读取,不管后面还有没有,也不读取空白字符,输入“ hello world !!”,最后在cin>>s 中读取到的只有s=“hello\0”;
读取操作可以多个对象一起进行:cin>>s1>>s2; 那么“ hello world !!”最后的结果是:s1=“hello”;s2=“world”;
使用getline(cin,s)读取一整行,getline()是无返回值函数,直接对s进行操作,所以不需要额外的空间承载返回值;getline()见到"\0"就结束输入,但是得到的字符串不含有换行符,所以如果要按行输出,那么就要自己手动加上换行操作。
empty() ,size()操作都是很显而易见的,带有返回值的函数,而且不需要传入参数,是string自带的一个成员函数,只需要使用点操作符调用即可:s.empty() s.size() ,前者返回一个bool变量,后者直接返回一个string::type_size的无符号整数变量,它能够存放下任何string的大小,是其专属的大小变量。若要在外部定义string::type_size类型的变量,可以用auto或者是decltype得到。
auto len=s.size(); decltype(s.size()) len;
比较运算符其实也没啥差别,但是比较大小的话,string中有一套规则(但是我自己总结下来:只要从头开始看起,第一个不相同的对应位置的字符比较大小就全权代表了这个字符串的大小,另外结束符号小于一切的字符!通用,不信你看):
* 如果两个string长度不同,较短的string对象每个字符都与较长的对象对应部分相同,那么较短的就短于长的;(like:abc < abcd )
* 如果在相应的位置不一样,那么就比较第一个相异的字符;(like: abd > abcd )相加相减操作:就直接加上咯,相当于拼接,反正string是动态增长长度的,所以随便你加多少。还有一种骚操作,那就是直接用原本的字符串变量直接加上一个字符串:但是记住,加号两边一定要有一个是string变量,两个字符串字面值直接加起来是违法操作!``` string s2=s1+"zhang"+"z"+"b" //从左到右相加,所以左边一直都是string变量!!
3、 处理string对象中的字符
字符操作形式 | 效果(s指string对象中的单个字符) |
---|---|
isalnum(s) | 判断字符是否是字母或数字; |
isalpha(s) | 判断字符是否是字母; |
iscntrl(s) | 判断字符是否是控制字符; |
isdigit(s) | 判断字符是否是数字; |
isgraph(s) | 判断字符是否是可打印的非空格字符; |
ispunct(s) | 判断字符是否是标点符号; |
isspace(s) | 判断字符是否是空白字符; |
isupper(s) | 判断字符是否是大写字母; |
isxdigit(s) | 判断字符是否是十六进制数; |
toupper(s) | 转换为大写字母; |
tolower(s) | 转换为小写字母。 |
采用基于范围的for循环实现:
for ( auto c:str) //此句的意思是:对于str中的每个字符进行拷贝
cout<<c<<endl;
上述只能实现读而不能实现存,因为是直接拷贝,所以无法对string的单个字符进行操作,但是如果是引用,就可以实现了。
for ( auto &c:str) //此句的意思是:对于str中的每个字符进行引用
{
cout<<c<<endl;
c=toupper(c);
}
利用这些特性,结合一个大家常见的下标运算符(这个点就不讲了,跟数组一样,烂大街了),我自己实现了一个把所有的英文单词的首字母改成大写的程序:
string w="hello boy I don't want to hurt you? baby~~~";
w[0]=toupper(w[0]);
for (decltype(w.size()) index=0; index < w.size(); ++index)
{
if (isspace(w[index]))
{
w[index+1]=toupper(w[index+1]);
}
}
cout<<"================================================\n"<<w<<"\n================================================\n"<<endl;
4、 标准库类型Vector定义与初始化
- vector是一种对象的集合,可以看作是一种容易,好比是房子,前面的string可以看做是教室排座位,每个座位上按序号只能坐入字符,不能是别的类型,并且多个字符可以组成一个小组,string对象就是一个小组;而vector就好比是大楼,可以安置的类型更加宽广。可以放入int,string,char等一些类型。因为在具体定义一个vector对象前不知道类型,所以我们称之为类模板,对其实施创建的时候称此过程为实例化,定义如下:
vector<int> ivec;
vector<string> strvec;
vector<vector<int> > vecivec;
因为vector是容纳对象的,所以不存在包含引用的vector;初始化如下:
vector<T> tvec; // Empty vector
vector<T> tvec(v1); // copy v1 to tvec
vector<T> tvec = v1 ; // 同上
vector<T> tvec(n,val); //tvec包含了n个相同T类型的val元素;
vector<T> tvec(n); //执行n次空的初始化;
vector<T> tvec{a,b,c,d ···}; //具体的初始化
vector<T> tvec={a,b,c,d ···}; //同上
注意如果是拷贝初始化的话,不同类型的vector是不能相互拷贝的!
5、 向vector中添加元素
对于vector这个容器,只能用专用的内置函数来对其增加元素,push_back具体用法如下:
vector<int> ivec;
for(int i=0;i!=100;++i)
{
ivec.push_back(i);
}
从上面我们可以知道,vector具有良好的动态增长的性能,所以一开始如果就限定其大小的话,是一种对特性的浪费,一开始初始化的时候就限定容量是不是一件明智的事情!(注意,其实上面的操作不是很符合规定,因为隐性规定:在循环体中改变了遍历序列的长度的操作不用for 循环,可能造成缓冲区溢出)
6、 其他vector操作
汇总的Vector操作,假设c是vector变量 | 效果 |
---|---|
c.clear() | 移除容器中所有数据。 |
c.empty() | 判断容器是否为空。 |
c.erase(pos) | 删除pos位置的数据 |
c.erase(beg,end) | 删除[beg,end)区间的数据 |
c.front() | 传回第一个数据。 |
c.insert(pos,elem) | 在pos位置插入一个elem拷贝 |
c.pop_back() | 删除最后一个数据。 |
c.push_back(elem) | 在尾部加入一个数据。 |
c.resize(num) | 重新设置该容器的大小 |
c.size() | 回容器中实际数据的个数。 |
c.begin() | 返回指向容器第一个元素的迭代器 |
c.end() | 返回指向容器最后一个元素的迭代器 |
我们可以看到,对于vector只有一种添加元素的办法,但是对于元素的读写,可以直接用下标表示法,这个数组,string是完全共同的, 但是请注意,千万不要给定一个不存在的下标,如果超出了vector变量的长度,那么毫无疑问你的,会产生严重错误!甚至可能导致 缓存区溢出!!
7、 迭代器
- 迭代器的介绍
- 迭代器类似于指针类型,它也提供了对对象的间接访问。
- 指针是c语言中就有的东西,迭代器是c++中才有的,指针用起来灵活高效,迭代器功能更丰富些。
- 迭代器提供一个对容器对象或者string对象的访问的方法,并且定义了容器范围。
- 对于上面介绍的几种标准库类型,都有内置的迭代器操作,所谓迭代器,就是两个地址。比如说下面的例子:
vector<int > v;
auto b=v.begin();
此时如果可以查看b的类型,你会发现其实就是个指针对象。只是其类型由编译器给定。我们只管auto 或者 decltype就好了!,另外还有一个end()函数返回尾后迭代器,没有什么实际意义,正如名字,是在最后一个元素的下一个位置,用于判断是否为空的容器(begin end指向一个位置的时候)
-
下面是一些关于迭代器的操作,其中iter就是迭代器,跟指针其实没啥区别
每种容器类型都定义了自己的迭代器类型,如vector
vector<int>::iterator iter;
语句定义了一个名为 iter 的变量,它的数据类型是 vector<int > 定义的 iterator 类型。每个标准库容器类型都定义了一个名为 iterator 的成员,这里的 iterator 与迭代器实际类型的含义相同。
- 前面的程序用vector::iterator 改变 vector 中的元素值。每种容器类型还定义了一种名为 const_iterator 的类型,该类型只能用于读取容器内元素,但不能改变其值。 当我们对普通 iterator 类型解引用时,得到对某个元素的非 const。而如果我们对const_iterator 类型解引用时,则可以得到一个指向 const 对象的引用,如同任何常量一样,该对象不能进行重写。
for (vector<string>::const_iteratoriter = text.begin();iter != text.end(); ++iter)
cout << *iter << endl; // printeach element in text
使用 const_iterator 类型时,我们可以得到一个迭代器,它自身的值可以改变,但不能用来改变其所指向的元素的值。可以对迭代器进行自增以及使用解引用操作符来读取值,但不能对该元素赋值。
- 使vector对象的迭代器失效的操作
- for中添加元素
- push_back或者改变容量的操作
记住一点:但凡是使用了迭代器的循环体,此时就不要像迭代器所属的容器进行添加元素的操作了!!!千万不要!!
- 迭代器的算术操作(跟指针没差别,只是是标准库自带的类型)
iter + n
iter - n
iter1 - iter2
vector<int>::iterator mid = vi.begin() +vi.size() / 2;
任何改变 vector 长度的操作都会使已存在的迭代器失效。例如,在调用 push_back 之后,就不能再信赖指向 vector 的迭代器的值了!!!!!!!~
正文之后
我只能看着这个配置流口水啊!!具体的购买详细和指导请看我另一篇文:万元台式机组装养成记
后来又加了三件配个套: