C++Primer第5版学习笔记(二)
1.using声明,我知道挺多同学写代码练手都要在源文件前几句直接加using namespace std;然而using语句并不是什么情况都这么使用的,稍后我们将会看到详细的用法。
2.标准库类型string,和C味的字符数组有区别的string,到底是怎么个构造,这章将会讲述。
3.标准库类型vector,vector和数组区别很大,这里将会提到,并引入一个“容器”的重要概念。
4.迭代器,迭代器用来代替下标这种传统方式访问容器或一些支持迭代器的类型。
以下几种初始化语句被string支持:
string s1;//创建了一个空的字符串,对象名为s1,类型为string类型。
string s2(s1);//是s2的值与s1的值相等。
string s2=s1;//同上一句,拷贝初始化。
string s3("value");//直接用字符串字面值初始化string类型的对象。
string s3="value";//字符串字面值转化为string类型变量并赋值给string。
string s4(n,'c');//直接初始化string,操作后s4拥有10个字符,每个字符的值都是'c'。
最后,其实string s5={"value"}和string s3="value"一样,也是合法的。不过大括号初始化是严格检测匹配的,比如int a={3.5};就是错误的。
3.string对象与字符串字面值相加 字符串字面值是字符数组类型,字符串字面值和string类型的对象在一起计算时会被自动转换为string类型。
4.其他支持的操作:包括下标运算符[ ]、重载的+、==、!=、<、>、<=、>=。
范围for语句用于遍历元素。形如:for(一个用于访问序列中基础元素的变量a : 被访问的序列对象b){statement..... blabla;}
首次初始化,变量a的值会被初始化为对象b序列中的第一个元素,迭代之后每次访问下一个元素,直到序列被完全访问结束。
可以使用auto &a的方式声明变量a,使变量绑定到具体的序列元素上,从而进行更改。如在for(auto a : str){}中,每次把a初始化的行为实质上是使a获得str每个元素的副本(拷贝),而for(auto &a : str){}这样的语句则使a成为了str对应的每个元素的"别名",从而可以修改str。
我们可以通过引用头文件<cctype>的形式处理每一个字符。这个头文件包含很多方便处理字符的函数。列举如下:
isalnum(c); //当c是字母或者数字时为真 isalpha(c); //当c是字母时为真
iscntrl(c); //当c是控制字符时为真
isdigit(c); //当c是数字时为真 isgraph(c); //当c不是空格但是可打印时为真
islower(c); //当c是小写字母为真
isprint(c); //当c可打印时为真 isupper(c); //当c是大写字符时为真
isxdigit(c); //当c是16位数字时为真
ispunct(c); //当c是标点符号时为真(一个字符除了控制字符,字母,数字,可打印空白就是标点符号)
isspace(c); //当c是空白时为真(空白包括空格,横向/纵向制表符,回车符,换行符,进纸符)
tolower(c); //把大写字符转换为小写字符,本来就是小写字符的不变,返回转换后的字符
toupper(c); //把小写字符转换为大写字符,本来就是大写字符的不变,返回转换后的字符
当我们在C++里面谈论容器这个概念时,我们应该知道容器是用来存储和组织一类特定对象的集合。下面提到的标准库类型vector,就是一个容器。
类模板一般用于按照模板规定好的规则生成不同的类。我们无需很麻烦的一个一个写类的定义,只需使用模板,给出指定的少量信息,类模板就会帮助我们自动生成一个我们可以直接使用的类。vector也是一个类模板。
通过类模板创建类的过程,或者通过类型创建对象的过程,就叫做实例化。
与string的定义和初始化一样,我们也可以使用多种方式定义和初始化一个vector对象。
以下几种初始化语句被string支持:
vector<Type> v1;//创建了一个空的vector容器,这个容器是Type类型对象的集合,这个集合名字叫做为v1,执行默认初始化。
vector<Type> v2(v1);//创建了一个叫做v2的vector容器,这个容器的内容和v1相同。
vector<Type> v2=v1;//同上一句,拷贝初始化。
vector<Type> v3{a,b,c};//v3包含了初始值个数的元素。
vector <Type> v4={a,b,c};//同上。
vector <Type> v5(n,val);//直接初始化这个容器,操作后v5拥有n个元素,每个元素的值都是val。
vector <Type> v5(n);//直接初始化这个容器,操作后v5拥有n个元素,每个元素的值都被默认初始化。
当我们使用圆括号()初始化对象时,IDE会认为我们在通过语句“构建”这个对象 ;当我们使用花括号{ }初始化对象时,IDE会认为我们在初始化对象。
当我们使用等号=初始化对象时,我们就执行了“拷贝初始化”;当我们不使用=初始化对象时,我们就执行了“直接初始化”。
但是当我们在花括号里面给一个不符合对象类型的值,IDE就会认为我们正在构建而非初始化对象,一个体现就是,vector<string> s1{10}; 这个语句中,10不能转换为string,因此被系统理解为“这个string容器里有10个元素”。当然,像vector <string>s1={10};这样的语句是错误的,因为=就应该是拷贝初始化了,然而10并不能够被转化为string因此也无法赋值。
1.向容器的后面添加元素:已存在vector<T> v;,可以使用 v.push_back(vector<T> a)的方式在集合v的尾部添加元素。
2.empty和size函数成员:已存在vector<T> v;,可以使用 v.empty()的方式判断v是否为空,可以使用v.size()的方式返回v的大小。
3.重载的运算符:vector支持的运算符包括下标运算符[ ]、重载的+、==、!=、<、>、<=、>=。这一点和string类似。
当使用vector <int> 创建类时,这个类的命名空间就是vector <int>,命名空间中的迭代器类型写作vector<int>::iterator。因为这个叫做"vector<int>::iterator"的迭代器类型名太长了也不好记,这里我们使用auto推导这个类型。用成员函数cbegin和cend可以推导出底层const迭代器,就是这个迭代器对迭代器指向的内容只读不写。第6章会详细说明。
一维数组声明形式:类型名 数组名[一个常量]。比如int a[15];这里这个数组的名字是a,有15个元素,每个元素都是int型的。再比如int *a[15];这里a数组的15个元素都是int *型的,即指向int的指针,这样的指针有15个,构成了一个数组。虽然有指针数组,但是不存在元素都是引用类型的数组。
一维数组的初始化方式就是花括号初始化,形如int a[n]={1,2,3};大括号里面的内容就是初始化列表,n为数组大小,可以缺省,缺省时数组长度由初始化列表的元素个数决定。当初始化列表的值的个数比数组长度小,数组剩下的元素被初始化为默认的值,比如对于有10个元素的int型数组,如果只给出第一个元素的值,后几个元素将被初始化为0。