c++primer

c++ primer
c++定义了几种基本内置类型(字符、整型、浮点数),也提供了自定义数据类型的机制;c++标准库定义了一些更加复杂的数据类型,如可变长字符串和向量。

第二章:变量和基本类型

2.1.基本内置类型

2.1.1算术类型

c++定义的基本数据类型包括算术类型(字符、整型数、布尔值、浮点数)和空类型。算术类型又分为两类:整型(int,字符,bool)和浮点型。
字符:c++提供了几种字符类型,基本的字符类型是char,一个char的空间应确保可以存放机器基本字符集中任意字符对于的数字值(就是说一个char的大小和一个机器字节一样)。wchar_t、char16_t、char32_t用于扩展字符集,wchar_t用于确保可以存放机器最大扩展字符中的任意一个字符,char16_t、char32_t为Unicode字符集服务。
整型:一个int(16位)至少和一个short(16位)一样大,一个long(32位)至少和一个int一样大,一个long long(64位)至少和一个long一样大。
1字节=8bit,字=4or8字节
浮点型:float:1个字(32bit),7个有效位
double:2个字(64bit),16个有效位
long double:3or4个字(96or128bit)被用于有特殊要求的硬件
带符号类型(正数、负数、0)和无符号类型(大于零的数):int、short、long、longlong都是带符号类型的,加上unsigned就可以得到无符号类型。

2.1.2类型转换

任意类型→bool:0→false,其它→true
bool→任意类型:false→0,true→1
浮点数→整型:仅保留小数点前部分
整数→浮点型:小数部分记为0,若整数所占空间超过浮点型的容量,可导致精度损失。
给无符号类型一个超出它表示范围的值时,结果为对无符号类型表示数值总数取模后的余数。
赋给带符号类型一个产出其表示范围的值是,结果是未定义的。
无符号数与有符号数相加时,先将有符号数转成无符号数(直接给无符号数赋一个负值),再相加。
带符号类型和无符号类型不要混用。

2.1.3字面值常量

每个字面值常量都对应着一种数据类型,字面值常量的形式和值决定了它的数据类型。
整型和浮点型字面值:0开头代表八进制、0开头代表十进制、0x开头代表十六进制。整型字面值具体的数据类型由它的值和符号决定。默认十进制字面值的类型是带符号数而八进制和十六进制字面值可能带符号也可能是无符号的。
默认浮点型字面值是一个double
字符和字符串字面值:

'a'//字符字面值
"hello world"//字符串字面值

字符串字面值的类型实际上是由常量字符构成的数组,编译器在资格字符串的结尾处加上了一个空字符(’\0’),因此,字符串字面值的实际长度比它的内容多1.
转义序列:分为不可打印字符和含有特殊含义的字符。
\n 换行符 ,\t 横向制表符 ,等等
指定字面值的类型:通过给添加前缀和后缀可以改变整型、浮点型、字符型字面值的默认类型。字符和字符串字面值:
前缀:u(char16_t) ,U(char32_t),L(wchar_t),u8(char)
整型字面值:
后缀:u orU(unsigned),l orL(long),ll orLL(long long)
浮点型字面值:
后缀:f orF(float),l or L(long double)
布尔字面值:true,false
指针字面值:nullptr

2.2变量

变量提供一个具名的、可供程序操作的存储空间。数据类型决定着变量所占空间的大小和布局方式、给值能存储的值的范围,以及变量能参与的运算。
通常情况下,对象是指一块能存储数据并具有某种类型的内存空间。(我们在使用对象这个词时,并不严格区分时类还是内置类型,也不区分是否命名或者只读)

2.2.1变量定义

初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象当前的值擦除掉,用一个新值带替代。
列表初始化:利用花括号把一个或多个初始值放在一起的初始化形式。
默认初始化:若变量定义时并没有指定初值,则变量被默认初始化,此时变量被赋予了默认值。
定义于函数体内的内置类型对象如果没有被初始化,则其值违背定义。类的对象如果没有显式地初始化,则其值由类确定。建议初始化每一个内置数据类型。

2.2.2变量声明和定义的关系

c++支持分离式编译(程序分割为如干个文件,每个文件可被独立编译)
声明使得名字位程序所知,定义负责创建与名字关联的实体。
定义还申请了存储空间,也可能位变量夫一个初始值。
若想声明一个变量而非定义它,应该在变量前添加关键字extern,而且不要显示地初始化变量。

extern int i;//声明i而非定义i
int j;//声明并且定义j

任何包含了显式初始化的声明即成为定义,变量能且只能被定义一次,但是可以被多次声明。
若在多个文件中使用同一个变量,变量的定义必须且只能出现在一个文件中,但是其它用到该变量的文件必须对其进行声明,绝对不能重复定义。
c++是一种静态类型语言,其含义是在编译阶段检查类型,其中检查类型的过程称为类型检查。

2.2.3标识符

c++的标识符由字母、数字、下划线组成,其中必须以字母或者下划线开头,长度无限制,但对大小写敏感。命名要规范,最好符合驼峰命名规则。

2.2.4名字的作用域

同一个名字在不同的作用域中可能指向不同的实体,名字的有效区域始于名字的声明语句,以声明语句所在的作用域末端结束。
内层作用域:被包含的作用域
外层作用域:包含别的作用域的作用域
允许早内存作用域中重新定义外层作用域已有的名字。

作用域操作符::

2.3复合类型

基于其他类型定义的类型,c++中有好几种复合类型,本文介绍引用和指针。

int a=10;//int 为基本数据类型,a为声明符

2.3.1引用

引用即是为对象起别名,定义引用时,程序把引用和它的初始值绑定在一起,一旦完成初始化,无法重新绑定到另外一个对象。
引用并非对象,它只是为一个已经存在的对象所起的另外一个名字。

int i=3;
int &a=i;//引用的定义
//引用的标识符要以&开头,并且引用类型要与其绑定的对象严格匹配
//引用类型的初始值必须是一个对象

2.3.2指针

与引用类似,指针也实现了对其他对象的间接访问,但指针又与引用相比有很多不同,1、指针本身是一个对象,允许对指针赋值和拷贝,在指针的生命周期内,它可以先后指向几个不同的对象2、指针无需在定义时赋初值,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。
引用不是对象,没有实际地址,所以不能定义指向引用的指针。指针需要和其指向的对象严格匹配(除开两种特殊情况)
指针的值(即地址)应属于下列4种状态之一:
1、指向一个对象
2、指向紧邻对象所占空间的下一个位置
3、空指针,意味着没有指向任何对象
4、无效指针,上述情况之外的其他值(试图拷贝或者以其它方式访问无效指针的值都将引发错误)
解引用操作仅适用于那些确实指向了某个对象的有效指针。
在声明语句中,& *用于组成符合类型,在表达式中,它们又变成了运算符。
空指针:不指向任何对象,生成空指针的三种方法:``

int *p1=nullptr;//nullptr是一种特殊类型的字面值,可以被转换成任意其它的指针类型
int *p2=0;//指针初始化为字面值0来生成指针
int *p3=NULL;//NULL预处理变量,它的值就是0
//建议初始化所有指针,并在对象定义完成之后再去定义指向它的指针。

赋值和指针:若想搞明白到底是改变了指针的值还是改变了指针所指向对象的值,应该记住赋值改变的永远是等号左侧的对象。
若指针存放的地址相等,则他们相等,否则他们不等。(相等有三种可能:都为空,都指向同一个对象,或者都指向了同一个对象的下一地址)
void * 指针
void *指针是一种特殊类型的指针,可用于存放任意对象的地址`

double obj=3.14,*pd=&obj;
void *pv=&obj;//void *能存放任意类型对象的地址,obj可以是任意类型的对象
pv=pd;//pv是可以存放任意类型的指针

void *来看内存空间也仅仅是内存空间,没办法获取内存空间中所存的对象(即不能进行解引用操作)。

2.3.3理解复合类型的声明

一条定义语句可以定义出不同类型的变量:

int i=1024,*P=&i;&r=i;//(*  &为类型修饰符)
int*p1,p2;//基本数据类型是int 而非int*   此条语句定义了指针p1,还有int 的p2

指向指针的指针:声明符中修饰符(& *)的个数并没有限制,当多个修饰符连写在一起时,按照逻辑解释即可。
以指针为类:指针时内存中的对象,像其它对象一样也有自己的地址,因此允许把指针的地址再存放到另一个指针当中。

  • 指针 **指向指针的指针 ***指向指针的指针的指针
int ival=1024;
int *pi=&ival;//pi指向一个int型的数
int **ppi=π//ppi指向一个int型的指针

指向指针的引用:
引用本身不是一个对象,因此不能定义指向引用的指针,但指针是对象,所以存在对指针的引用。

int i=42;
int *p;//p是一个int型的指针
int *&r=p;//r是一个对指针p的引用
r=&i;//r引用了一个指针,因此给r赋值&i  就是令p指向了i
*r=0;//解引用r得到i,也就是p指向的对象,将i的值改为0

要理解r的类型是到底是什么,最简单的办法就是从右至左阅读r的定义,离变量名最近的符号对变量名有直接的影响

2.4const 限定符

const对象一旦创建其值就不能再改变,所以const对象必须初始化。
默认状态下,const对象仅在文件内有效,当多个文件中出现了同名的const变量时,其实在等同于在不同文件中分别定义了独立的变量。
若想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。

2.4.1const的引用

const int ci=1024;
const int &r1=ci;//引用及其对应的对象都是常量
r1=42;//错误,r1是对常量的引用
int &r2=ci;//错误,试图让一个非常量引用指向一个常量对象

引用的对象是常量还是非常量可以决定其所能参与的操作,却无论如何都不会影响到引用和对象的绑定关系本身。
初始化和对const的引用:
2.3.1提到引用的类型必须与其所引用对象的类型一致,但是有两个例外。
例外一:在初始化常量引用时,允许用任意表达式作为初始值,只要表达式的结果能转换成引用的类型即可。尤其,允许为一个常量引用绑定非常量的对象、字面值,甚至是个一般表达式。

int i=42;
const int &r1=i;//允许将const int &绑定到一个普通int上
const int &r2=42;//正确,r2是一个常量引用
const int &r3=r1*2
double dval =3.14;
const int & ri=dval;等价于const int temp=dval;//让双精度浮点数生成一个临时的整型变量 cosnt int &ri=temp;//让ri绑定这个临时量

对const的引用可能引用一个并非const的对象

int i=42;
int &r1=i;//引用r1绑定对象i
const int &r2=i;//r2也绑定了对象i,但是不能通过r2修改i的值
r1=0;//r1并非常量,i的值修改为0
r2=0;//错误,r2是一个常量引用,不能修改

2.4.2指针和const

指向常量的指针不能用于改变其所指对象的值,要想存放存放常量的地址,只能使用指向常量的指针。

const double pi=3.14;//pi是个常量,它的值不能改变
double *ptr=π//错误:ptr是一个普通指针
const double *cptr =π//正确,cptr可以指向双精度常量。只能使用指向常量的指针
*cptr=42;//错误:不能给*cptr赋值

指向常量的指针仅仅要求不能通过该指针改变对象的值,而没有规定那个对象的值不能通过其它途径改变。
const指针:
常量指针必须初始化,而且一旦初始化完成,则它的值(即存放在指针中的那个地址)就不能再改变了。

int errNum=0;
int *const curErr=&errNumb;//常量指针,不变的是指针本身的值
//curErr本身是一个常量对象,而*意味着是一个常量指针,int确定了常量指针指向的是一个int对象。
const double pi=3.14;
const double *const pip=π//pip是一个指向常量对象的常量指针。

指针本身是一个常量并不意味着不能通过指针修改其所指对象的值,能否这样做完全依赖于所指对象的类型。

2.4.3顶层const

指针本身是不是常量以及指针所指对象是不是常量是两个相互独立的问题;
顶层const:表示指针本身是个常量,更一般的,可以表示任意的对象是个常量,这一点对任何数据类型都适用,如算术类型、类、指针。
底层const:表示指针所指对象是一个常量,与指针和引用等复合类型的基本类型部分有关

但是指针既可以是顶层const,也可以是底层const。

const int *const p3=p2;//第一个const表示指针所指对象是一个常量为底层const,第二个const表示指针是个常量为顶层const
const int &r=ci;

2.4.4constexpr和常量表达式

常量表达式:指值不会改变并且在编译过程就能得到计算结果的表达式。
一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定。

const int max=20;//是
const int sz=get_size();//不是,因为sz 的具体值知道运行时才能获取

constexpr变量:
允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个变量表达式。(一般来说,认定变量是以一个常量表达式,那么就把它声明成constexpr类型)
字面值类型:算术类型、引用和指针都属于字面值类型;自定义类、IO库、string类型不属于字面值类型,就不能把他们定义为constexpr。
一个constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象。
指针和constexpr:
限定符constexpr仅对指针有效,与指针所指对象无关:

const int *p=nullptr;//p是一个指向常量的指针
constexpr int *q=nullptr;//q是一个指向整数的常量指针

constexpr把它所定义的对象置为了顶层const。

constexpr const int *p=&i;//p是常量指针,指向整型常量i
constexpr int *p1=&j;//p1是常量指针,指向整数j

2.5处理类型

2.5.1类型别名

类型别名是一个名字,它是某种类型的同义词,有两种方法可以定义类型别名,1、传统的方法是使用typedef 2、别名声明来定义类型的别名

typedef double wages;//wages是double的同义词
typedef double base,*p;//base是double的同义词,p是double*的同义词

using SI=sales_item;//SI是sales_item的同义词

类型别名等价于类型,两者在任何地方可以互换。
指针、常量和类型名:

typedef char * pstring;//pstring 是char *的别名,即pstring是指向char型的指针
const pstring cstr=0;//cstr是指向char的常量指针
const pstring *ps;//ps是一个指针,它指向的对象是 指向char的常量指针

2.5.2auto类型说明符

利用auto就可以让编译器替我们去分析表达式所属的类型,auto让编译器通过初始值来推算变量的类型,显然,auto定义的变量必须有初始值。

auto i=0,*p=&i;//正确:i是整数,p是整型指针
auto sz=0,pi=3.14;//错误,一个整型,一个浮点型

2.5.3复合类型、常量和auto

  • List item

auto一般会忽略顶层const,而底层const会保留下来

2.5.3decltype类型指示符

希望从表达式的类型推断出要定义的变量的类型,但是又不想用该表达式的值初始化变量。decltype的作用是选择并返回操作数的数据类型。

const int ci=0,&cj=ci;
decltype(ci) x=0;//x的类型是const int
decltype(cj) y=x;//y的类型是const int &,y绑定到了x
decltype(cj) z;//错误:z是一个引用,必须初始化

decltype和引用:
如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。

int i=42,*p=&i,&r=i;
decltype(r+0) b;//正确,r+0的结果是int,b也是一个int
decltype(*p) c;//错误,解引用操作将会得到引用类型,得到int &,引用类型必须初始化

decltype使用的是一个不加括号的变量,则得到的结果就是该变量的类型;
如果加上了一层或多层括号,编译器会把它当成一个表达式。

decltype((i)) d;//错误,双层括号的结果永远是引用,引用必须初始化
decltype(i) e;//正确,e是一个未初始化的int

2.6自定义数据结构

数据结构:把一组数据元素组织起来然后使用他们的策略和方法。
类的数据成员定义了类的对象的具体内容,可以为数据成员提供一个类内初始值,创建对象时,类内初始值将用于初始化数据成员。没有初始值的成员将会被默认初始化。
为了确保各个文件中的类定义一致,类通常被定义在头文件中,而且类所在的头文件的名字应该与类的名字一样。
头文件通常只包含那些只能被定义一次的实体,如类、const、和constexpr变量 等。
预处理器:是在编译之前执行的一段程序,可以部分地改变我们所写的程序。
头文件保护符:依赖于预处理变量(已定义的和未定义)
#define指令把一个名字设定为预处理变量
另外两个指令则分别检查某个指令的预处理变量是否定义:
#ifdef 当且仅当 变量已定义时为真
#ifndef当且仅当变量未定义时为真

第三章 字符串、向量和数组

vector 存放的是某种给定类型对象的可变长序列。

3.1命名空间的using声明

头文件不应该包含using声明。
std::cin的含义是:作用域操作符(::)编译器应该从操作符的左侧名字所示的作用域中寻找右侧那个名字。

3.2标准库类型string

如何初始化类的对象由类的本身决定,一个类可以定义很多种初始化对象的方式。

string s1;
string s2(s1);//拷贝初始化
string s2=s1;//拷贝初始化
string s3("value");//直接初始化
string s3="value";//拷贝初始化
string s4(n,'c');//直接初始化
string s8=string(10,'c');//拷贝初始化,对多个值进行初始化的情况,非要用拷贝初始化的方式也可以,此时需要显式地创建一个(临时)对象用于拷贝。

直接初始化:不使用等号,
拷贝初始化:用等号(=)初始化一个变量,实际上执行的是拷贝初始化。

os<<s;//将s写到输出流os中,返回os
is>>s;//将is中读取数据串赋给s,字符串以空白分隔,返回is
getline(is,s);//从is中读取一行赋给s,返回is

读取未知数量的string对象:

string word;
while(cin>>word)//反复读取,直至到达文件末尾
{
cout<<word<<endl;//逐个输出单词,每个单词后面紧跟一个换行
}

使用getline读取一整行

string line;
while(getline(cin,line))//每次读入一整行,直至到达文件末尾
{
cout<<line<<endl;
}

string::size_type类型是size函数的返回类型,size_type是一个无符号类型的值,而且能存放任何string对象的大小。允许通过auto和decltype来推断变量的类型。
string的比较规则:1、若两个string的长度相同,并且短的string与长的string对应的字符都相同,就说短string对象小于较长string对象
2、若两个string对象在某些对应位置上不同,其实是在比较第一组相异字符的结果
字面值和string相加:
因为标准库允许把字符字面值和字符串字面值转换成string对象,所以在需要string对象的地方就可以使用这两种字面值来替代。
当string对象和字符字面值及字符串字面值混在一再语句中,必须确保每个加号运算符(+)的两侧运算对象至少有一个是string。

isspace(c);//判断是否空白
toupper(c);//若是小写,则输出对应的大写

c++兼容了c,c语言的头文件形如name.h,c++则将这些命名位cname。

//范围for语句
//for(declaration:expression)
//statement
//expression是一个对象,declaration定义一个变量,该变量会用于访问序列中的
//基础元素,每次迭代,declaration部分的变会被初始化为expression部分的下一
//个元素值
string str("some string");
for(auto c:str)//对于str中的每个字符
cout<<c<<endl;//输出当前字符,后面紧跟一个换行符

若想使用范围for改变字符串中的字符,需把循环变量定义成引用类型。

string s("hello world");
for(auto &c:s)//对于s中的每个字符
{
c=toupper(c);//c是一个引用,因此赋值语句将改变s中字符的值
}

3.3标准库类型vector

c++语言既有类模板,也哟哟函数模板,vector是一个类模板。编译器根据模板创建类或者函数的过程被称作实例化。当使用模板时,需要指出编译器应把类或者函数实例成何种类型。

vector<T> v1;
vector<T> v2(v1);
vector<T> v2=v1;
vector<T> v3(n,val);
vector<T> v4(n);//v4包含了n个重复执行了值初始化的对象
vector<T> v5{a,b,c....};
vector<T> v5={a,b,c....};

不能等价使用的初始化方式的两种类外:
1、使用拷贝(=)初始化时,只能提供一个初始值
2、若提供的是一个类内初始值,则只能用拷贝初始化或使用花括号的形式初始化。

vector<string> v6{"hi"};//错误:不能使用字符串字面值构建vector对象

vector对象(以及string对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素。

3.4迭代器介绍

有效迭代器或者指向某个元素,或者指向容器中尾元素的下一位置。
end迭代器被称为尾后迭代器,特殊情况下(如果容器为空,则begin和end返回的是同一个迭代器)。
end返回的迭代器并不实际指示某个元素,所以不能对其进行递增或者解引用操作。
c++要养成使用迭代器!=的习惯,就不用在意是那种容器类型。

vector<int>::const_iterator it3;//it3只能读元素,不能写元素
string::const_iterator it4;//it4只能读字符,不能写字符
iter1-iter2;//两个迭代器相减的结果是它们之间的距离,参与运算的两个迭代器必
//须指向的是同一个容器中的元素或者尾元素的下一位置。其类型是名为difference_type的带符号型整数。

3.5数组

数组的元素是对象,因此不存在引用的数组。
显式初始化数组。

const unsinged sz=3;
int ia[sz]={0,1,2};
int a2[]={0,1,2};
int a3[5]={0,1,2};
string a4[3]={"hi","bye"};

字符数组有一种额外的初始化形式,可用字符串字面值对此类数组初始化。
使用这种方式时,一定要注意字符串字面值的结尾处还有一个空字符,这个空字符也会像字符串的其它字符一样被拷贝到字符串数组当中去:

char a1[]={'C','+','+'};//列表初始化,没有空字符
char a2[]={'C','+','+','\0'};//列表初始化,含有显式的空字符
char a3[]="C++";//自动添加表示字符串结束的空字符,表示这里有4个字符

数组不允许拷贝和赋值,不能将数组的内容拷贝给其它数组作为初始值,也不能使用数组为其它数组赋值。
理解复杂的数组声明:

int *ptrs[10];//ptrs是含有10个整型指针的数组
int &refs[10];//错误定义:不存在引用的数组。
int (*Parray)[10]=&arr;//Parray是一个指针,它指向一个int数组,数组中包含10个元素;可理解为Parray是指向一个含有10个整数的数组。
int (&arrRef)[10]=arr;//arrRef是一个引用,它引用的对象是一个大小为10的数组,数组中元素的类型为int;可理解为arrRef引用一个含有10个整数的数组。

数组下标通常定义为size_t类型,size_t是一种机器相关的无符号类型,它被设计得足够大以便能表示内存中任意对象得大小。
c风格字符串:字符串存放在字符数组中并以空字符结束。以空字符结束的意思是在字符串最后得一个字符后面跟着一个空字符(’\0’)。一般用指针来操作这些字符串。
任何出现字符串字面值的地方都可以用空字符结束的字符数组来替代:
c++应当尽量使用vector和迭代器,避免使用内置数组和指针;应当尽量使用string,避免使用c风格的基于数组的字符串。

3.6多维数组

多维数组指的是数组的数组,对于二维数组来说,常把第一个维度称为行,第二个维度称为列。
使用范围for语句处理多维数组

int cnt=0;
for(auto &row:ia)
{
for(auto &col:row)
{
col=cnt;
cnt++;
}
}

getline首先读取输入流的内容直到遇到换行符停止,然后将读入的数据存入string对象,最后返回istream对象。
实例化:编译器生成一个模板类或函数的过程。
值初始化:是一种初始化过程,内置类型初始化为0,类类型由类的默认构造函数初始化;只有当类包含默认构造函数时,该类的对象才能被值初始化。
a->b运算符等价于(*a).b;
<<运算符:该运算符负责输出string对象中的字符
运算符>>:输入运算符,负责读入一组字符,遇到空白就停止,读入的内容赋给运算符右侧的运算对象,该运算对象是一个string对象。

第四章、表达式

表达式由一个或多个运算对象组成,对表达式求值将得到一个结果,字面值和变量是最简单的表达式,其结果就是字面值和变量的值。

4.1基础

小整数类型(char、bool、short等)通常会被提升成较大的整数类型(主要是int)。
重载运算符:当运算符作用于类类型的运算对象时,用户可以自行定义其含义,这种定义事实上是为已存在的运算符赋予了另外一层含义,所以称之为重载运算符。
左值:指那些求值结果为对象或函数的表达式。一个表示对象的非常量左值可以作为赋值运算符的左侧运算对象。
右值:指一种表达式,其结果是值而非值所在的位置。
归纳:当一个对象被用作右值的时候,用的是对象的值(内容),当对象被用作左值的时候,用的是对象的身份(在内存的位置)。

4.2算术运算符

算术运算符(+、-、*、\、%)的运算对象和求值结果都是右值。
在求表达式之前,小整数类型的运算对象被提升为较大的整数类型,所有的运算对象最终会转换成同一类型。
取余的运算对象必须是整数类型。

4.3逻辑和关系运算符

关系运算符(!、<、<=、>、>=、==、!=)作用于算术类型或指针类型;
逻辑运算符(&&、||)作用于任意能转换成布尔值的类型;此两类运算符的运算对象和求值结果都是右值。
&&:逻辑与运算符,只有当左侧运算对象为真时,才对右侧运算对象求值。
||:逻辑或运算符,只有当左侧运算对象为假时,才对右侧运算对象求值。

4.4赋值运算符

赋值运算符的左侧运算对象必须是一个可修改的左值。
赋值运算符的结果是它的左侧运算对象,并且是一个左值。相应的。结果的类型就是左侧运算对象的类型。如果赋值运算符的两个运算对象类型不相同,则右侧运算对象将转成左侧运算对象的类型。

4.5递增和递减运算符

递增和递减运算符有两种形式:前置版本和后置版本。
++i:先执行++的操作,再把递增后的值给到对象
i++:先把值给对象,再进行递增
递增递减运算符必须作用于左值运算对象。
后置递增运算符的优先级高于解引用运算符,*p++等价于 *(p++);
建议使用前置运算符。

4.6成员访问运算符

ptr->mem等价于(*ptr).mem;点运算符优先级高于解引用运算符。

4.7条件运算符

cond?expr1:expr2;
当条件运算符的两个表达式都是左值或者能转换成同一种左值类型时,运算的结果时左值,否则运算的结果是右值。

4.8位运算符

位运算符作用于整数类型的运算对象,并把运算对象看成是二进制位的集合。位运算符提供检查和设置二进制位的功能。
运算符(~、<<、>>、&、^、|)位求反、左移、右移、位与、位异或、位或。强烈建议仅将位运算符用于处理无符号类型。
移位运算符:
左移运算符<<:在右侧插入值为0的二进制位;
右移运算符>>:若运算对象是无符号类型:在左侧插入值为0的二进制位;
若运算对象是带符号类型,在左侧插入符号的副本或值为0的二进制位。

4.9sizeof运算符

sizeof运算符返回一条表达式或者一个类型名字所占的字节数。sizeof运算符满足右运算律,其所得值是一个size_t类型的常量表达式。

Sales_data data,*p;
sizeof(Sales_data);//存储Sales_data类型的对象所占空间的大小
sizeof data;//data类型的大小,即sizeof(Sales_data)
sizeof p;//指针所占的空间
sizeof *p;//p所指类型的空间大小,即sizeof(Sales_data)
sizeof data.revenue;//Sales_data的revenue成员对应类型的大小
sizeof Sales_data::revenue;//另一种获取revenue大小的方式

对char或者类型为char的表达式执行sizeof运算;
对引用类型执行sizeof运算得到被引用对象所占空间的大小;
对指针指向sizeof运算得到指针本身所占空间的大小;
对解引用指针执行sizeof运算得到指针指向的对象所占的空间大小
对数组执行sizeof运算得到整个数组所占空间的大小,等价于对数组中所有的元素各执行一次sizeof运算并将所得结果求和。
对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多空间。

4.10逗号运算符

按照从左向右的顺序依次求值。逗号运算符真正的结果是右侧表达式的值,如果右侧运算对象是左值,那么最终求值的结果也是左值。

4.11类型转换

若两种类型可以互相转换,那么它们就是关联的。
隐式转换:根据类型转换规则设法将运算对象的类型同一后求值。
隐式转换发生的情况:
1、比int类型小的整数值首先提升为较大的整数类型
2、在条件中,非布尔值转成布尔类型
3、初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型
4、算术运算或者关系运算的运算对象有多种类型,需要转换成同一种类型
5、函数调用时也会发生类型转换

4.11.1算术转换

算术转换:把一种算术类型转换成另外一种算术类型,算术转换的规则定义了一套类型转换的层次,其中运算符的运算对象将转换成最宽的类型。
当表达式中既有浮点类型也有整数类型时,整数值将转换成相应的浮点类型。
整型提升:负责把小整数类型转换成较大的整数类型
bool,char,signed char,unsigned char,short,unsigned short能提升到int类型。
无符号类型的运算对象:
一个运算对象为无符号类型、另一个运算对象为带符号类型:
1、无符号类型≥带符号类型,则带符号的运算对象转换成无符号的
2、无符号类型<带符号类型,(1)、若无符号能存入带符号中,则无符号转换成带符号(2)、若无符号不能转换成带符号,则带符号转换成无符号。

4.11.2其它隐式类型转换

数组转换成指针:在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针。

int ia[10];//定义了一个10元素数组
int *ip=ia;//ia转换成指向数组首元素的指针

指针的转换:常量整数值0和字面值nullptr能转换成任意指针类型;指向任意非常量的指针能转换成void*;指向任意对象的指针能转换成const void*.

4.11.3显式转换

强制类型转换:一种显式的类型转换。
cast-name(expression);
type:转换的目标类型
expression:要转换的值
若type是引用类型,则结果是左值。
cast-name(static_cast、dynamic_cast、const_cast、reinterpret_cast其中的一种),dynamic_cast支持允许时的类型识别。
static_cast:任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。
const_cast:只能改变运算对象的底层const
reinterpret_cast:把运算对象的内容解释为另外一种类型。

第五章:语句

c++提供了一组控制流语句以及支持更复杂的执行路径。

5.3条件语句

有两种按条件执行的语句:一种是if语句,它根据条件决定控制流;
另外一种是switch语句,计算一个整型表达式,根据这个整型表达式的值从几条执行路径选一条。

switch(ch)
{
case 'a':
break;
case 'b':
break;
case 'c':
break;
}

break语句用来中断当前的控制流;此例中,break语句将控制权转移到switch语句外面。case分支后面的break语句不能省略。
default标签:若没有任何一个标签能匹配上switch表达式的值,程序将执行紧跟在default标签后面的语句。
c++规定不允许跨过变量的初始化语句直接跳转到该变量作用域内的另一个位置。

5.4迭代语句

5.4.1while语句:

当不确定到底要迭代多少次时,使用while循环比较合适;还有就是想在循环结束后访问循环控制变量。

5.4.2for语句:

for(int i=0;i<10;++i);
for运行的步骤:
1、循环开始时,先执行int i=0;
2、接下来判断是否i<10,若条件为真,则执行for循环体的内容;否则循环终止。
3、最后执行++i;第2、3步重复执行,直到不满足条件终止。

5.4.3范围for语句

for(auto &r:v)
{

}
auto 定义一个变量,序列中每个元素都能转换成该变量的类型;每次迭代都会重新定义循环控制变量,并将其初始化成序列中的下一个值,之后才会执行语句块。

5.4.4 do while语句

do while 先执行依次循环,再检查条件。

5.5跳转语句

c++提供了4种跳转语句:break、continue、goto、return

5.5.1break语句

break负责终止离它最近的while、do while、for或者switch语句,并从这些语句之后的第一条语句开始继续执行。

5.5.2continue语句

continue语句终止最近的循环中的当前迭代并立即开始下一次迭代;continue只能出现在for、while、do while 循环的内部。

5.5.3goto语句

语法形式:goto label;
带标签语句是一种特殊的语句,在它之前有一个标识符以及一个冒号:
如:end :return;