命名空间的 using 声明
using namespace::name;
1.每个名字都需要独立的 using 声明;
2.头文件不应包含 using 声明:
因为头文件的内容会拷贝到所有引用它的文件中去,如果头文件里有某个using声明,那么每个使用了该头文件的文件就都会有这个声明。(可能会引起名字冲突)
标准库类型 string
标准库类型 string 表示可变长的字符序列,使用 string 类型必须首先包含 string 头文件。作为标准库的一部分,string定义在命名空间 std 中。
定义和初始化 string 对象
如何初始化类的对象是由类本身决定的。
初始化 string 对象的方式:
string sl;// 默认初始化,s1是一个空串
string s2(s1);// s2是s1的副本
string s2 = sl;// 等价于s2(s1), s2是s1的副本
string s3("value");// s3是字面值"value"的副本,除了字面值最后的那个空字符外
string s3 = "value";// 等价于s3("value"), s3是字面值"value"的副本
string s4(n, 'c');// 把s4初始化为由连续n个字符c组成的串
如果使用等号(=)初始化一个变量,实际上执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去。与之相反,如果不使用等号,则执行的是直接初始化。
string s5 = "hiya";//拷贝初始化
string s6("hiya");//直接初始化
string s7(10, 'c');//直接初始化,s7的内容是cccccccccc
string 对象上的操作
读写 string 对象
在执行读取操作时,string对象会自动忽略开头的空白(即空格符、换行符、制表符等)并从第一个真正的字符开始读起,直到遇见下一处空白为止。
string s;//空字符串
cin >>S;//将string对象读入s,遇到空白停止
cout <<s<< endl;//输出s
//如上所述,如果程序的输入是“ Hello wor1d !”(注意开头和结尾处的空格),则输出将是“Hello”,输出结果中没有任何空格。
读取未知数量的 string 对象
string word;
//反复读取,直至到达文件末尾
while (cin >> word){
cout<< word << endl;//逐个输出单词,每个单词后面紧跟一个换行
}
使用 getline 读取一整行
用getline函数代替原来的>>运算符在最终得到的字符串中保留输入时的空白符。
getline函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止(注意换行符也被读进来了),然后把所读的内容存入到那个string对象中去(注意不存换行符)。
getline只要一遇到换行符就结束读取操作并返回结果,哪怕输入的一开始就是换行符也是如此。如果输入真的一开始就是换行符,那么所得的结果是个空string.
string line;
//每次读入一整行,直至到达文件末尾
while (getline (cin,line)){
cout<< line<< endl;
}
getline 会将换行符也读入,但是不将换行符存入 string 对象。即触发 getline() 函数返回的那个换行符实际上被丢弃掉了。
string 的 empty 和 size 操作
empty 函数根据string对象是否为空返回一个对应的布尔值。
size函数返回string对象的长度(即string对象中字符的个数)。
if( !line.empty())//判断是否为空
if(line.size()>80)//判断长度是否超过80
string::size_type 类型
存放 string 类的 size 函数返回的是一个string::size_type 类型。string::size_type 类型是一个无符号类型的值而且能够存放下任何string对象的大小。
auto len = s.size();// len 的类型是 string::size_type
如果一条表达式中已经有了size()函数就不要再使用int,否则可能出现意想不到的问题。
比较 string 对象
1.如果两个string对象的长度不同,而且较短string对象的每个字符都与较长string对象对应位置上的字符相同,就说较短string对象小于较长string对象。
2.如果两个string对象在某些对应的位置上不一致,则string对象比较的结果其实是string对象中第一对相异字符比较的结果。
两个string对象相加
string sl = "hello,",s2 ="world\n";
string s3 = s1 +s2;// s3的内容是hello, world\n
s1 += s2;//等价于sl = s1 +s2
字面值和 string 对象相加
允许把字符字面值和字符串字面值转换成string对象。
字符串字面值与string是不同的类型。
处理 string 对象中的字符
主要处理三个问题:1.如何获取字符本身;2.要知道能改变某个字符的特性;3.遇到某个条件处理停止。
cctype 头文件中定义的函数可以帮助解决这些问题
C++程序应该使用名为cname的头文件而不使用name.h的形式,其实两者在内容上并没有明显区别,主要区别是在名为 cname 的头文件中定义的名字从属于命名空间 std,而定义在名为 .h 的头文件则不然。
基于范围的 for 语句
范围 for (range for) 语句:遍历给定序列中的每个元素并对序列中的每个值执行某种操作。
for(declaration : expression)
statement
其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。
string str ( "some string");//每行输出str中的一个字符。
for (auto c : str)//对于str中的每个字符
cout << c<< endl;//输出当前字符,后面紧跟一个换行符
使用范围for语句改变字符串中的字符
如果想要改变string对象中字符的值,必须把循环变量定义成引用类型。
string s("Hello world!!!");
//转换成大写形式。
for (auto &c : s)//1对于s中的每个字符(注意:c是引用)
c= toupper(c);//c是一个引用,因此赋值语句将改变s中字符的值
cout<< s << endl;
使用下标可以执行随机访问string[index]
,注意:C++标准并不要求标准库检测下标是否合法。
对 string 的最后一个字符进行索引:string[string.size()-1]
。
标准库类型 vector
标准库类型vector表示对象的集合,其中所有对象的类型都相同。集合中的每个对象都有一个与之对应的索引,索引用于访问对象。
C++语言既有类模板(class template),也有函数模板,其中vector是一个类模板。
vector是模板而非类型,由vector生成的类型必须包含vector中元素的类型,例如vector<int>。
定义和初始化 vector 对象
初始化 vector 对象的方法
在某些情况下,初始化的真实含义依赖于传递初始值时用的是花括号还是圆括号。
vector<int> V1 (10);//v1有10个元素,每个的值都是0
vector<int> V2{10};//v2有1个元素,该元素的值是10
vector<int> v3(10,1); //v3有10个元素,每个的值都是1
vector<int> v4{10,1}; // v4有2个元素,值分别是10和1
- 如果用的是圆括号,可以说提供的值是用来构造(construct) vector对象的。
- 如果用的是花括号,可以表述成我们想列表初始化(list initialize)该vector对象。
如果初始化时使用了花括号的形式,但是提供的值又不能用来列表初始化,就要考虑用这样的值来构造vector对象了。
vector<string> v5{"hi"};//列表初始化:v5有一个元素
vector<string> v6("hi");//错误:不能使用字符串字面值构建vector对象
vector<string> v7 {10};//v7有10个默认初始化的元素
vector<string> v8{10,"hi"};// v8有10个值为"hi"的元素
vector 支持的操作
vector对象(以及string对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素。
迭代器
迭代器类似于指针,也提供了对对象的间接访问。
使用迭代器
*iter;//返回迭代器iter所指元素的引用
iter->mem;//解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem
++ite;//令iter指示容器中的下一个元素
--iter;//令iter指示容器中的上一个元素
//判断两个迭代器是否相等(不相等),如果两个迭代器指示的是同一个元素或者它们是同一个容器的尾后迭代器,则相等;反之,不相等
iter1 == iter2;
iter1 != iter2;
迭代器类型
拥有迭代器的标准库类型使用 iterator 和 const_iterator 来表示迭代器的类型:
vector<int>::iterator it;//it能读写vector<int>的元素
string::iterator it2;//it2能读写string对象中的字符
vector<int>::const_iterator it3;//it3只能读元素,不能写元素
string::const_iterator it4;//it4只能读字符,不能写字符
const_iterator和常量指针差不多,能读取但不能修改它所指的元素值。相反,iterator的对象可读可写。
bgein 和 end 运算符
begin 和 end 返回的具体类型由对象是否是常量决定,如果对象是常量,begin和end返回const_ iterator;如果对象不是常量,返回iterator。
vector<int>v;
const vector<int> cv ;
auto itl = v.begin();// it1的类型是vector<int>::iterator
auto it2 = cv.begin (); // it2的类型是vector<int>::const_iterator
//cbegin 和 cend 都是 const_iterator 类型
auto it3 = v.cbegin();// it3的类型是vector<int>; :const_iterator
迭代器运算
string 和 vector 支持的迭代器运算:
数组
与vector不同的地方是,数组的大小确定不变,不能随意向数组中增加元素。因为数组的大小固定。
不清楚元素的确切个数的情况下,一般使用vector 。
定义和初始化内置数组
和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值。
数组的维度应该是已知的,也就是说维度必须是一个常量表达式。
字符串字面值的结尾处还有一个空字符,这个空字符也会被拷贝到字符数组中。
char a1[] = {'C', '+', '+'};//列表初始化,没有空字符
char a2[] = {'C', '+','+','\0'};//列表初始化,含有显式的空字符
char a3[] = "C++";//自动添加表示字符串结束的空字符
const char a4[6]= "Daniel";//错误:没有空间可存放空字符!
数组不允许拷贝和赋值。但是可以一个元素一个元素进行处理。
就数组而言,是从数组的名字开始按照由内向外进行阅读:
int *ptrs [10];//ptrs是含有10个整型指针的数组
int &refs [10]=/*?*/;//错误:不存在引用的数组
int (*Parray)[10] = &arr;//Parray指向一个含有10个整数的数组
int (&arrRef)[10] = arr;//arrRef引用一个含有10个整数的数组
访问数组元素
使用 size_t 类型作为数组小标。size_t 是一种机器相关的无符号类型,它足够大能表示内存中任意对象的大小。(在 cstddef 头文件中定义)
指针和数组
对数组的元素使用取地址符就能得到指向该元素的指针。
string nums[] ={"one","two","three");//数组的元素是string对象
string *p= &nums[0];//p指向nums的第一个元素
string*p2 =nums; //等价于p2 = &nums[0]
标准库函数 begin 和 end
因为数组不是类类型,所以 begin 和 end 不是成员函数。
// pbeg指向arr的首元素,pend指向arr尾元素的下一位置
int *pbeg = begin(arr) , *pend = end(arr) ;
尾后指针不指向具体的元素,因此尾后指针不能执行解引用和递增操作。
指针运算
两个指针相减的结果是它们之间的距离。
auto n = end(arr) - begin(arr); //n 是 arr 中元素的数量
两个指针相减的结果的类型是一种名为 ptrdiff_t 的标准库类型,和 size_t一样,ptrdiff_t 也是一种定义在 cstddef 头文件中的机器相关的类型。因为差值可能为负值,所以 ptrdiff_t 是一种带符号类型。
只要两个指针指向同一个数组的元素,或者指向该数组的尾元素的下一位置,就能利用关系运算符对其进行比较。如果两个指针分别指向不相关的对象,则不能比较它们。
数组内置的下标运算符所用的索引值不是无符号类型,这一点与vector和string 不一样。只要指针指向的是数组中的元素(或者数组中尾元素的下一位置),都可以执行下标运算:
int *p= &ia[2];// p指向索引为2的元素
int j=p[1];// p[1]等价于*(p+ 1),就是ia[3]表示的那个元素
int k= p[-2];// p[-2]是ia[0]表示的那个元素
C 风格字符串
c 风格字符串不是一种类型,而是一种写法,约定俗成的想法。
与旧代码的接口
1.允许使用字符串字面值来初始化 string 对象。
string s("Hello World");//s 的内容是 Hello world
任何出现字符串字面值的地方都可以用以空字符结束的字符数组来替代。
但不能反过来使用 string 对象初始化字符数组,要用 c_str() 函数将 string 对象转化为 c 风格字符串
char *str = s;//错误:不能用string对象初始化char*
const char *str = s.c_str();//正确
2.允许使用数组来初始化 vector 对象。
指明要拷贝区域的首元素地址和尾后地址:
int int_arr[] ={0,1,2,3,4,5);
// ivec有6个元素,分别是int_arr中对应元素的副本
vector<int> ivec(begin(int_arr), end(int_arr));
多维数组
允许使用花括号括起来的一组值初始化多维数组:
int ia[3][4] ={//三个元素,每个元素都是大小为4的数组
{0,1,2,3},//第1行的初始值
{4,5,6,7},//第2行的初始值
{8,9,10,11}//第3行的初始值
};
内层嵌套的花括号不是必须的:
//没有标识每行的花括号,与之前的初始化语句是等价的
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
显式初始化部分元素
//显式地初始化每行的首元素
int ia[3][4]= {{0 },{4 },{8}};
//显式地初始化第1行,其他元素执行值初始化
int ix[3][4] -{0,3,6,9};
多维数组的下标引用
多维数组的每个维度对应一个下标运算符。
//用arr的首元素为ia最后一行的最后一个元素赋值
ia[2][3] = arr[0][0][0];
int (&row)[4] = ia[1];//把row绑定到ia的第二个4元素数组上
使用范围 for 语句处理多维数组
for 语句中改变元素值要显示使用 & 符号声明为引用类型
size_t cnt = 0;
for (auto &row : ia)//对于外层数组的每一个元素
for (auto &col : row){//对于内层数组的每一个元素
col = cnt;//将下一个值赋给该元素
++cnt;//将cnt 加 l
}
for (const auto &row : ia)1/对于外层数组的每一个元素
for (auto col : row)//对于内层数组的每一个元素
cout << col <<endl;
要使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。
因为如果不声明为引用类型,编译器会自动将控制变量转换为指向数组首元素的指针,在内层的循环就变得不合法了。
当程序使用多维数组的名字时,也会自动将其转换成指向数组首元素的指针(多维数组实际上是数组的数组)。
int ia [3][4];//大小为3的数组,每个元素是含有4个整数的数组
int(*p)[4]= ia;//p指向含有4个整数的数组
p = &ia[2];//p指向ia的尾元素
//声明中,圆括号必不可少:
int *ip[4];//整型指针的数组
重要术语
- 拷贝初始化(copy initialization) 使用赋值号(=)的初始化形式。新创建的对象是初始值的一个副本。
- difference_type 由string和 vector定义的一种带符号整数类型,表示两个迭代器之间的距离。
- 实例化(instantiation) 编译器生成一个指定的模板类或函数的过程。
- **以空字符结束的字符串(null-terminatedstring)**是一个字符串,它的最后一个字符后面还跟着一个空字符(‘\0’)。
- 尾后迭代器(off-the-end iterator) end函数返回的迭代器,指向一个并不存在的元素,该元素位于容器尾元素的下一位置。