2.6 自定义数据结构
- 可以使用struct或者class定义自己的数据结构
- 为了确保头文件定义的一致性,类通常防在头文件中。
- 确保头文件多次包含仍然能够正常执行的技术是预处理器,预处理在编译前执行的一段程序,当预处理器看到#include的时候,该模块会使用响应的头文件进行替换。
- 还有一项预处理功能是头文件保护符,头文件保护符依赖于预处理变量,预处理变量有两种状态,一种是已定义,一种是未定义。
#ifndef DEMO_H 当且仅当变量未定义使为真,一旦为真,则一致执行到endif
#define DEMO_H 把一个名字设置为预处理变量。
。。。。。
#endif
三.字符串向量和数组
3.1 命名空间的using声明
- 可以使用using声明语句来代替命名空间访问名字的前缀
- 每个名字都需要独立的using声明
- 头文件不应该包含using声明
3.2标准库类型string
- string表示可变长字符序列,string类型存放在<string>头文件中
定义和初始化string - 初始化方式有如下几种:
#include<string>
string s1; #默认初始化为一个空串
string s2 = s1; #s1是s2的副本
string s2(s1); ##s1是s2的副本,同上
string ss("hello world") ; #ss是字面值hello world的副本除了最后的\0
string s3 = "hello world"; #同上
string s4(10, 'c'); #把s4初始化为连续10个c字符的序列
直接初始化和拷贝初始化
- 使用=实际上是拷贝初始化,否则使用直接初始化
- 一般情况下,直接初始化和拷贝初始化都用,但是上面s4只能使用直接初始化
string常用操作(全)
#include<string>
os << s ; # 将字符串s输出到输入流,返回os
is >> s ; # 从输入读字符串赋值给s,返回is
getline(is, s) #从is中读取一行赋值给s,返回is
s.empty()
s.size() #返回string对象中字符的个数,返回值是一个string::size_type
s[]
s1+s2
s1=s2
s1==s2
s1!=s2
<,>,<=,>=
- while在读取输入的时候遇到文件结束或错误返回false,否则为true
- getline从输入流读入内容知道遇到换行符(换行符也读入进来),然后把读到的内容存到string对象中(存储的时候不存储换行符)。
** C++处理字符的函数**
#include<cctype>
isalnum(c)
isalpha(c)
...
- C++11 支持对string使用foreach循环遍历
- 处理部分字符可以使用索引的方式取得响应字符
string s = "Hello world" ;
char c = s[3] ;
- C++的索引不对响应数据类型的数据进行越界检查,此时取得的值是不确定的。
- 使用下标的时候一定要检查下标的合法性。
3.3标准库类型vector
C++语言既有类模版也有函数模版,编译器根据模版创建类或函数的过程叫做实例化,当使用模版的时候,需要确定对象要被实例化成什么类型。
定义和初始化vector对象
vector<T> v1; #默认构造函数,初始化的v1为空vector
vector<T> v2(v1); #将v1中的元素全部复制到v2中,v1中对象的类必须与v2的类相同
vector<T> v2=v1; #等价于vector<T> v2(v1)
vector<T> v3(n); #v3含有n个重复执行了值初始化的对象
vector<T> v4(n, v); #v4含有n个值为v的元素
vector<T> v5{a,b,c…}; #v5包含了初始值个数的元素,每个元素被赋予相应的初始值
vector<T> v5={a,b,c…}; #等价于vector<T> v5{a,b,c…} 列表初始化
- 列表初始化还是元素数量取决于使用花括号{}还是圆括号(),一般花括号表示列表初始化,圆括号表示调用构造函数。但是另一方面,如果花括号中提供的值不能被用于列表初始化,那么则把他看成构造对象。
向vector添加元素
push_back(value) ;
- 对vector的要求
- 如果循环体内部包含要在vector对象添加元素的语句,则不能使用范围for循环
- 不能用下标形式添加元素
- vector 下标操作不对越界进行检查,所以要小心下标越界
其他vector操作
v.empty()
v.size()
v.push_back(t)
v[n]
v1 = v2
v1 = {a,b,c}
v1 == v2
v2 !=v2
< <= >=
3.4 迭代器介绍
- 迭代器允许对容器中对象进行访问,他是一个对象,提供访问容器的方法
- 迭代器有有效和无效之分。有效的迭代器指向元素或容器尾元素的下一个位置,其他的是无效的
- 使用begin和end成员方法获得迭代器,begin成员函数负责指向第一个元素的迭代器,end函数负责指向尾元素的下一位置的迭代器,也就是该迭代器指向不存在的尾后元素
- 迭代器相关运算
*iter #返回迭代器iter所指元素的引用
iter->mem #解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem
++iter #令iter指示容器的下一个元素
--iter #令iter指示容器的上一个元素
iter1 == iter2 #判断两个迭代器是否相等(不相等),如果两个迭代器指示是同一个元素或者它们是同一个容器的尾后迭代器,则相等,反之,不相等
iter1 != iter2
- 一般我们不精确的知道迭代器的类型,实际上我们也无需知道。
- beding和end返回的值是不是const_iterator取决于具体要迭代的对象是不是常量,如果是常量,则返回const_iterator 。
- C++定义了箭头操作符->,该操作符把解引用和成员访问两个操作结合到了一起。
(*it).mem == it->mem ;
- 某些对vector对象的操作会使得迭代器失效
- 对vector进行push_back
- 任何一种可能改变vector对象的操作
- 迭代器相关运算
iter + n #迭代器加上一个整数,根据实际执行的加减决定迭代器向前或向后移动abs(n)个位置,仍旧得到一个迭代器
iter - n
iter += n #类似于iter = iter + n
iter -= n #类似于iter = iter - n
iter1 - iter2 #返回指向同一容器的两个迭代器的距离,类型difference_type,有正负
>,>=,<,<= #根据迭代器指向的容器位置先后来判断关系式是否成立
3.4数组
指针和数组
- 使用数组的时候编译器一般会把数组转换为指针
- 对数组元素使用取地址符,得到指向该元素的指针
- C++为数组提供了begin和end用于得到数组的头元素和尾后元素的指针
- 指针支持迭代器的所有运算,+,-等,如上迭代器相关运算
- 两个指针相减返回他们之间的距离,返回的类型是ptrdiff_t标准库类型,空指针也允许如此操作。两个空指针相减等于0。这种运算的指针必须是同一个对象的指针。
- 对数组的下标操作,编译器会自动执行上面的转换,转换为指针的操作。
int a[] = {1,2,3,4};
a[n] == *(a+n);
- 标准库类型string和vector也有部分不同,标准库类型的下标值必须是无符号类型,但是内置的下标运算没有这种要求。
int k = p[-2] == p[0]
3.5 C风格字符串
- C风格字符串必须以空字符作为结尾,否则错误。
- 对C风格字符串的操作要使用响应的库函数
兼容旧有C代码
c_str()函数返回从风格字符串