字符串
记一下string的几种没用过的初始化操作,赋值不能这么搞
string s1("value");
string s2(s1);
string s3(6,'z');
string s4=string(6,'a');
上面没咋提拷贝初始化,带等号的都是拷贝初始化,这里都是直接初始化
getline(cin, line);
可接受句子,遇到换行符则停止,并且换行符也会被读入,但是这个换行符又被丢弃了
while(getline(cin, line)){
cout << line << endl;
}
while(cin>>line){
cout << line << endl;
}
从上述两个例子里可以看出getline确实读入了换行符而且确实又把它给丢弃了。
注意注意注意!!
string.size()这个函数返回的值的类型并不是int,而是string::size_type类型的,并且非常值得注意的是这个类型他是无符号类型的,用这个直接带入一些式子中可能会有意想不到的结果的;
尤其要注意的是s.size()<n这样的结构。~!!
关于string大小的判断,比较第一对相异的字符大小即可,若没有相异的,谁长谁大,或者就是相等
可以用字符字面值和字符串字面值转换成string
当string对象和字符字面值以及字符串字面值混在一起使用是,必须确保加号两边至少一个是string
像string a = “aaa”+“666”+b;这样子的也是错误的!
上图所示的c值的是单个字符。
关于头文件中.h和c开头的区别
内容上完全一样,只不过c开头的属于命名空间std的而.h的却并不是,而是在include文件里的
使用范围for语句改变字符串中的字符一定要用引用。
为什么?
因为不用引用的话改变的只是c的值而已。
如果某个索引是带符号类型的值的话,将会自动转换为无符号的string::size_type类型。
运算的两个运算对象都为真,则逻辑与结果为真;否则结果为假。对这个运算符来说最重要的一点是,C++语言规定只有当左侧运算对象为真时才会检查右侧运算对象的情况。
关于vector
没有用过的vector初始化
vector<T> v1;
vector<T>v(5); //--->表示的是v中初始化了五个值而不是二维vector
vector<T> v2(v1);
vector<T> v2 = v1;
vector<T> v3(n, val)
vector<T> v4(n)
vector<T> v5{a, b, c..}
vector<T> v6={a, b, c..}
关于vector< string> v{5, “bo”}和vector< int> v{5, 6}的区别,仔细看。
v.size 返回的也是size_type类型
运算符的比较方法也和string一毛一样,即字典序顺序
注意:vector不能以下标形式添加元素。只能用来访问已存在的元素
关于迭代器
严格上讲,string对象不属于容器类型,但是string支持很多与容器类似的操作,比如string同样支持迭代器。
V.end() 指的是v尾元素的下一位置
因此end成员返回的迭代器常被称作是尾后迭代器或者叫尾迭代器。
注意一下一种特殊的情况,当容器为空时,begin和end返回的是同一个迭代器,并且还值得注意的是,此时的begin和end返回的是同一个迭代器,都是尾后迭代器。
迭代器的数据类型方法比较陌生,写的很少,所以写一个
vector<int>::iterator it;
string::iterator it2;
还有一种叫只读迭代器的,就是只能读元素
vector<int>::const_iterator it3;
如果iterator的对象是一个常量,那只能用const_iterator,若不是,那用什么都可以。
有些时候,为了便于专门得到const_iterator类型的返回值,c++11新标准引入了两个新函数,分别是cbegin和cend.用法同begin和end,只不过只读。
对于一个字符串组成的vector来说,可以这么写
*(it).empty();
注意注意注意:这个前面的这个圆括号必不可少!
为了简化上述表达式,就有一种箭头运算符->,把解引用和成员访问两个操作结合在了一起。
即it->empty()和*(it).empty()是一个意思。
先得知道知道有些操作比如push_back会使迭代器失效。后面的章节会详解。
所以要记牢,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。
关于迭代器运算
目前知道的string和vector可以使用各种迭代器运算,加减了啥的都行
使用迭代器运算的一个经典二分,easy。
关于数组
定义数组时,维度必须是常量表达式。
unsigned cnt = 42;
constexpr unsigned sz = 42;
int a[sz];//正确,因为sz是常量表达式
int b[cnt];//错误
int s[get_size];//只有当get_size是constexpr时正确,否则错。
数组不允许拷贝和赋值!!!
当然有些编译器支持赋值,这就是所谓的编译器扩展,但为了兼容性,不写这种。
关于数组的指针和指针的数组和数组的引用和引用的数组
- 引用的数组:实际上数组,但是数组属于一个对象,不能引用,所以这样是不行的!
- 数组的引用:实际上是引用,该引用了一个数组。int (&arrRef)[10] = arr;
- 指针的数组;实际上是数组,这个数组中含有的都是整型指针。int *ptrs[10];
- 数组的指针:实际上是指针,这个指针,指向了一个数组而已。int (*Parray)[10] = arr;
数组最好从内向外读;
使用数组下标时,通常定义为size_t类型,注意这是无符号类型,
关于数组,小心溢出
string p = num; //num是数组,等价于p = &num[0];
所以呢num就相当于&num[0];
但是但是但是,当使用decltype的时候却有点不对。即
decltype(num) 这个东西返回的类型却并不是指针,而是某个大小的数组。
指针也是迭代器。vector和string迭代器支持的运算,数组的指针全部支持!
可以通过int *e = &arr[10];这种方式将e作为尾迭代器。
尽管能计算得到尾指针,但是容易出错,所以c++11引入了两个名为begin和end的函数。用法见例子就行。
int ia[] = {0,1,2,3,4,5};
int *beg = begin(ia);
int *last =end(ia);
尾指针不可以解引用,虽然编译器不会报错,但是这属于使用错误,就不能这么用。
指针指向数组可以使用下标,来个例子就够了
int *p = &a[2];
int j =p[1]; // p[1] 等价为*(p+1)。
int k = p[-2]; //等价为*(p-2);也就是指向a[0];
也很明显,这里的索引不是无符号型的
char ca[] = {'C', '+', '-', '\0'}; //这里必须最后手动加上空字符,不然strlen函数会出错。
cout << strlen(ca);
strcmp(ca1, ca2); 前大正后大负,一样为0;
不能使用string来初始化指向char的指针。
char *str = s;//s为string,所以这是错误的。
const char *str = s.c_str();//这是正确的。
用c_str()函数可以做到,但是一定只能返回const char类型的,否则会报错。
虽然我们不能用vector来初始化数组,但是我们可以用数组来初始化vector:
vector<int> v(begin(b), end(b));
也可以只是数组的一部分:
vector<int> v(b+1, b+3);
关于多维数组
严格上讲,c++没有多维数组,通常所说的多维数组其实就是数组的数组罢了,谨记
比如 int ia[3][4];
ia可以这么理解:ia是一个含有三个元素的数组,每个元素本身又都是含有四个元素的数组。
for(auto &c : a)
for(auto v : c)
这个地方注意了,第一行for必须用引用,不用引用就出错,为什么?
因为c不是引用类型,所以编译器在初始化c的时候,会自动将这些数组形式的元素,转换为指向该数组内首元素的指针!
对于二维数组ai[3][4]
附上三种不同遍历方法:
版本一
for(int (&p)[4] : ia){
for(int &q : p)
cout << q;
cout << endl;
}
版本二
for(int p=0; p<3; p++){
for(int q=0; q<3; q++)
cout << ia[p][q];
cout << endl;
}
版本三
for(int (*p)[4] = ia; p != ia+3; ++p){
for(int *q = *p; q != *p+3; ++q)
cout << *q;
cout << endl;
}
一般来说,应该优先选用标准库提供的类型,之后再考虑C++语言内置的低层的替代品数组或指针。
最后
确定这样简单明了????
来源:CSDN
作者:zhan-z
链接:https://blog.csdn.net/qq_40962234/article/details/104561618