第三章
1.头文件不应包含using声明,因为头文件的内容会拷贝到所有引用它的文件中去。
2.初始化string对象的方式:
string s1; //默认初始化,s1是一个空串
string s2(s1); //s2是s1的一个副本
string s2=s1; //等价于s2(s1)
string s3("value"); //s3是字面值"value"的副本,除了字面值最后那个空字符外
string s3="value"; //等价于s3("value")
string s4(n,'c'); //把s4初始化为由连续n个字符c组成的串
用等号初始化一个变量,执行的是拷贝初始化,不使用等号,是直接初始化。
对于多个初始值一般用直接初始化,如s4,如要用拷贝初始化,需要显示创建一个临时对象用于拷贝:
string s8=string(,'c');
这条语句等价于:
string temp(,'c');
string s8=temp;
3.读取一行使用getline(),getline()参数是一个输入流和一个string对象,会读入换行符,把所读内容存入string对象时不存换行符。getline()也会返回它的流参数,因此可以作为判断条件。
4.string类及其他大多数标准库类型都定义了几种配套的类型,这些配套类型体现了标准库与机器无关的特性。string::size_type就是其中一种,是size函数返回值的类型。string::size_type是无符号类型的值并且能足够放下任何string对象的大小。
5.如果一条表达式中已经有了size()函数就不要再使用int了,这样可以避免int和unsigned可能带来的问题。
auto len=line.size(); //len的类型是string::size_type
if(line.size()<n) //如果n为负值,结果几乎为真,n会转化为一个比较大的无符号值
6.字面值和string对象相加:
string s1="hello",s2="world";
string s3=s1+","+"world"; //正确,右边的值可以转换成string
string s4="hello"+","+s2; //错误,试图把两个字符串字面值加在一起
7.建议使用c++版本的c标准库头文件,即将name.h改为cname,标准库中的名字总能在std中找到,使用.h,就必须记住哪些是c语言继承,哪些是c++独有的。
8.范围for语句遍历给定序列中的每个元素并对序列中的每个值执行某种操作:
string s("Hello World!!!");
decltype(s.size()) punct_cnt=;
for (auto c : s)
if (ispunct(c))
++punct_cnt;
cout<<punct_cnt<<" punctuation characters in "<<s<<endl;
要改变字符串中的字符,必须把循环变量定义成引用类型:
string s("Hello World");
for (auto &c:s)
c=toupper(c);
cout<<s<<endl;
9.string的下标运算符([])接收的输入参数是string::size_type,如果某个索引是带符号类型的值将自动转换成string::size_type表达的无符号类型。
10.逻辑与运算符(&&)只有当左侧运算对象为真时才会检查右侧运算对象。
11.vector是容器,也是类模板:
vector<int> ivec;
vector<string> svec;
vector<vector<string>> file;
vector是模板而非类型,由vector生成的类型必须包含vector中元素的类型,由于引用不是对象,所以不存在包含引用的vector。
在早期C++标准中如果vector的元素还是vector,必须在外层的vector对象的右尖括号和其元素类型之间添加一个空格,如应写成vector<vector<int> >。
12.C++初始化方式例外情况是1:使用拷贝初始化时(即使用=)只能提供一个初始值;2.如果提供类内初始值,只能使用拷贝初始化或使用花括号的形式初始化;3.如果提供的是初始元素值的列表,只能使用花括号,不能使用圆括号。
13.只提供vector对象容纳的数量,此时库会创建一个值初始化的元素初值,并赋给容器中的所有元素。有两个限制:
(1)如果vector对象中元素的类型不支持默认初始化,我们必须提供初始的元素值。
(2)只提供元素的数量而没有初始值,只能用直接初始化:
vector<int> vi=;//错误,只能用直接初始化的形式
14.使用圆括号,值是用来构造vector对象的;使用花括号,值是用来列表初始化vector对象的,初始化过程会尽可能把花括号的值当成元素初始值的列表来处理,只有无法执行列表初始化时才会考虑其他初始化方式(如构造vector对象):
vector<int> v1();//v1有10个元素,每个都是0
vector<int> v2{};//v2有1个元素,是10
vector<int> v3(,);//v3有10个元素,每个的值都是1
vector<int> v4{,};//v4有2个元素,分别是10和1
vector<string> v5{"hi"};//列表初始化:v5有一个元素
vector<string> v6("hi");//错误:不能使用字符串字面值构造vector对象
vector<string> v7{};//v7有10个默认初始化的元素
vector<string> v8{,"hi"};//v8有10个"hi"元素
15.vector用push_back添加元素,vector对象能高速增长,在创建vector时设定其大小性能可能更差,只有一种例外,就是所有的元素值都一样时。
16.范围for语句体内不应该改变其所遍历序列的大小。
17.用下标的形式去访问一个不存在的元素将引发错误,不过这种错误不会被编译器发现,而是在运行时产生一个不可预知的值,缓冲区溢出就是这类错误,确保下标合法的方式是使用范围for语句。
18.begin返回指向第一个元素的迭代器,end返回指向尾元素的下一位置的迭代器,如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器
*iter 返回迭代器iter所指元素的引用
iter->mem 解引用iter并获取该元素名为mem的成员,等价于(*iter).mem
++iter 令iter指示容器中下一个元素
--iter
iter1==iter2 如果两个迭代器指示的是同一个元素或者他们是同一个容器的尾后迭代器,
iter1!=iter2 则相等;反之,则不相等
所有标准库容器的迭代器都有==和!=,但大多数没有<运算符
19.iterator和const_iterator表示迭代器类型,const_iterator和指向常量的指针一样(书上有误,书上写的常量指针),能读取但不能修改它所指元素的值。
如果vector或string对象是常量,只能使用const_iterator,否则,iterator和const_iterator都可以。
20.迭代器有三种含义:1.迭代器概念本身;2.容器定义的迭代器类型;3.某个迭代器对象。
某个类型是迭代器当且仅当它支持一套操作,这套操作使得我们能访问容器的元素或者从某个元素移动到另外一个元素。
每个容器类定义了一个名为iterator的类型,该类型支持迭代器概念所规定的一套操作。
21.begin()和end()返回const_iterator或iterator,由对象决定。
cbegin()和cend()返回const_iterator,不管对象是否是常量。
22.箭头运算符it->mem和(*it).mem表达一个意思。
23.凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。
24.string和vector迭代器支持的运算有:
iter+n;
iter-n;
iter+=n;
iter-=n;
iter1-iter2;
>,>=,<,<=;
25.数组的维度必须是常量表达式,默认情况下,数组的元素被默认初始化,如果在函数内部定义了某种内置类型的数组,默认初始化会令数组含有未定义的值。
string strs[get_size()];//get_size是constexpr时正确,否则错误
26.字符数组特殊性:
const char a[]="aaa";//错误,没有空间存放空字符
27.复杂的数组声明:
int *ptrs[];//10个整型指针的数组
int &ptrs[];//错误,不存在引用的数组
int (*parray)[]=&arr;//parray指向一个含有10个整数的数组
int (&aref)[]=arr;//aref引用一个含有10个整数的数组
int *(&array)[]=ptrs;//array是数组的引用,该数组含有10个指针
从数组名字开始由内向外阅读,&array说明是一个引用,然后观察右边,引用的对象是一个大小为10的数组,再观察左边,数组元素是指向int的指针。
28.数组下标定义成size_t类型,size_t是一种机器相关的无符号类型。
当数组或其他类似数据结构的下标越界并试图访问非法内存区域时,会产生缓冲区溢出错误。
29.使用数组时编译器会把它转换成指针,使用数组作为auto变量的初始值时,推断得到的类型是指针而非数组:
int a=;
int *b=&a;
int ia[]={,,};
auto ia2(ia);//相当于auto ia2(&ia[0])
ia2=b;//ia2指向b所指对象,即a
当用decltype时上述转换不会发生:
decltype(ia) ia3={,,};
ia3=b;//错误,ia3是数组