(26)头文件中不应该包含using声明。因为头文件的内容会拷贝到所有引用它的文件中去,如果头文件里有某个using声明,那么每个使用了该头文件的文件都会有这个声明。对于某些程序来说,由于不经意间包含了一些名字反而可能产生始料未及的名字冲突。
(27)string
初始化:
拷贝初始化:使用等号初始化变量
直接初始化:不使用等号
string s1;
string s2(s1); //直接初始化
string s2 = s1; //拷贝初始化
string s3("value"); //直接初始化
string se = "value"; //拷贝初始化
string s4(n,'c'); //直接初始化
//C++11:for
string str("some string");
for(auto c:str)
cout<<c<<endl;
如果想要改变string对象中字符的值,必须把循环变量定义成引用类型。
string::size_type,无符号整数,返回size大小,主要用于体现机器无关特性
(28)vector是一个类模板,其中所有对象的类型都相同
使用vector,必须包含头文件
#include <vector>
using std::vector;
size_type的使用: vector<int >::size_type
vector对象的下标运算符可用于访问已存在的元素,不能用于添加元素
(29)迭代器:有迭代器类型同时拥有返回迭代器的成员
auto b = v.begin(),e = v.end();
end成员负责返回指向容器 “尾元素的下一位置 ”,仅做个标记
//可读可写
vector<int> iterator it;
string::iterator it2;
//只读
vector<int> ::const_iterator it;
string::const_iterator it2;
c++11引入两种新函数cbegin cend
auto it3 = v.cbegin();//it3类型是vector<int>::const_iterator
(*it).empty() //解引用it,然后调用结果对象的empty成员;(*it).mem 与it->mem表达意思相同
*it.empty() //错误,it是个迭代器,没有empty成员
迭代器的关系运算符:> ,<,>=,<=,如果某迭代器指向容器位置在另一迭代器所指位置之前,则说前者小于后者。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一个位置。
(30)数组:数组中元素的个数在编译时应该是已知的,因此维度必须是一个常量表达式
unsigned cnt = 42;//不是常量表达式
constexpr usigned sz = 42;
int *parr[sz]; //含有42个整型指针的数组
string bad[cnt]; //错误,cnt不是常量表达式
string strs[get_size()];//当get_size是constexpr时正确,否则错误
不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。
int a[] = {1,2,3};
int a2[] = a;//错误,不允许使用一个数组初始化另一个数组
a2 =a;//错误,不能把一个数组直接赋值给另一个数组
int *ptrs[10];//ptrs是含有10个整型指针的数组
int &refs[10];//错误,不存在引用的数组
int (*Parray)[10];//Parray是一个指向含有10个整数的数组
int (&arrRef)[10];//arrRef引用含有10个整数的数组
就数组而言,由内向外的顺序可帮助我们更好地理解Parray的含义:
首先看括号括起来的部分,*Parray意味着Parray是个指针,接下来看右边,可知道Parray是个指向大小为10的数组的指针
int *(&arry)[10] = ptrs; //arry是数组的引用,该数组有10个指针
(31)指针和数组
string nums[] = {"one","two","three"};
string *p = &nums[0];// p指向nums的第一个元素
然而,数组还有一个特性,在很多用到数组名字的地方,编译器都会自动地将其替换为指向数组首元素的指针:
string *p2 = nums;//等价于p2 = &nums[0]
int ia[] = {0,1,2,3,4,5,6,7,8,9}; //ia是一个含有10个整数的数组
auto ia2(ia); //ia2是个整数指针,指向ia的第一个元素
//尽管ia是一个由10个整数构成的数组,但当使用ia作为初始值时,编译器实际执行的初始化过程类似于如下
auto ia2(&ia[0])
c++11引入两个名为begin和end的函数,不过数组毕竟不是类类型,因此这两个函数不是成员函数。正确的使用形式是将数组作为它们的参数。这两个函数定义在iterator头文件中。
int ia[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = begin(ia);
int *end = end(ia);
使用for处理多维数组
for(const auto &row:ia) for(auto col:row) cout<<col<<endl;
将外层循环的控制变量变成了引用类型,这是为了避免数组被自动转成指针。假设不用引用类型,循环如下for(const auto row:ia) for(auto col:row) cout<<col<<endl;
无法编译,row不是引用类型,所以编译初始化row时会自动将这些数组形式的元素转换成指向该数组内首元素的指针。这样row的类型就是int*,显然不合法。
(32)sizeof运算符:返回一条表达式或一个类型名字所占的字节数,所得值是size_t
(33)强制类型类型转换
C++提供了关键字explicit,可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生。声明为explicit的构造函数不能在隐式转换中使用。
cast-name<type>(expression);
type是转换目标类型,expression是要转换的值。如果type是引用类型,则结果是左值
cast-name是static_cast dynamic_cast const_cast reinterpret_cast
(34)
区分左值右值的真正说法是:能否用“取地址&”运算符获得对象的内存地址。
对于临时对象,它可以存储于寄存器中,所以是没办法用“取地址&”运算符;
对于常量,它可能被编码到机器指令的“立即数”中,所以是没办法用“取地址&”运算符;
这也是C/C++标准的规定。
通常
= 左边表示引用,实质上就是内存地址。
= 右边表示数据,从内存取的数据,直接给的数据,或者计算出来的数据。
内存地址本身也是数据,也可以放到内存中。相当于C++中说的指针。