标签:
C++Primer第5版学习笔记(一)
第一、二章的重难点内容
本篇文章主要记录了我在学习C++Primer(第5版,中文版)中遇到的重难点及其分析。因为第一、二章比较简单,因此这里合并这两章我遇到的问题。
第一章 开始
这一章在第一部分之前,是一个helloworld式的章节,包含基本的函数,io流以及类的介绍。
知识点1:P19,1.5,文件重定向
可以在windows下的cmd中或者mac,linux系统的终端窗口中用输入命令的形式执行程序并使它从一个文件中读入数据,再把标准输出改为输出到另一个文件里。
如果我们编译好的程序为a.exe,程序需要输入两个整数,整数之间用逗号间隔,然后需要检测到EOF(End_Of_File,文件结束符)程序才会停止。这时为了文件重定向,我们可以新建一个文本文档(b.txt),接着我们在文档里记录”1,2“然后保存起来,保存后系统会在这个文件的末尾自动添加文件结束符。然后再新建一个空白文本文档(c.txt),使a.exe,b.txt,c.txt在同一个目录下。这时打开终端,转到这个目录,输入a <b.txt>c.txt回车确认。之后我们就实现了文件重定向了。
但是不被缓冲的cerr的数据没法被重定向到指定文件里。
(交叉引用:P23,第一章小结)cerr和clog的内容都不会马上输出,都会先被存储到缓存区里面,因此你可以在缓存区被刷新之前,指定(重定向)它们输出的位置。而cerr是直接输出的,因此只能做标准输出,不能重定向。读cin,程序意外终止,使用endl都会刷新缓存区。
第I部分:C++基础
第二章 变量和基本类型
C++Primer由四部分组成,这里是比较基础的第一部分,这部分共有六章。主要讲变量,变量类型,定义变量的各种情况和规则以及变量之间互相转换的规则。
知识点2:P30,2.1.1,C++语言关于类型的规定
C++语言的基本类型的设定与硬件紧密相关,因此很多类型的内存尺寸也都只是给了一个范围,其实各家IDE(LLVM,GCC,Visaul C++)的实现都是在范围内,具体的实现细节都是不确定的。
其中bool最小尺寸未定义,char最小尺寸是8位,wchar_t和char16_t的最小尺寸都是16位,char32_t的最小尺寸是32位,int的最小尺寸是16位,long和long long的最小尺寸分别是32位和64位,对于浮点型数据的表现尺寸是按照精度计算的,其中float的最小尺寸精度是小数点后6位,double和long double的最小尺寸精度则是小数点后10位(实际可能比这个精度要大一些)。int不得小于short,long不得小于int,long long不得小于long。float,double,long double也应该是精度递增(或者相同)的关系。
知识点3:P32,2.1.2,类型转换
程序自动执行的类型转换操作发生在程序里IDE预期我们使用A类型但是实际上我们使用B类型的时候,B类型的对象会自动转换为A类型的,如果没法转换,程序就会报错。
我们先看赋值操作里表达式里面发生的自动转换,赋值操作A=B中,等号左边的A被叫做左值,B被叫做右值,程序期待事情是你给定的右值和左值类型完全相同。如果不相同,这里就会发生强制的类型转换,即把B的类型转化为A的类型。如果把一个超出左值类型表达范围的数赋值给左值,左值又是一个无符号类型,比如unsigned char c=-1;这时-1(整型,负的)会转化为无符号字符型,初始值对无符号类型表示数值总数取模,然后求余数,这个余数就是转化后的数。
因为C++没有明确规定有符号类型的数应该如何表示,因此如果把一个超出左值类型表达范围的数赋值给左值,左值又是一个有符号类型,这种行为的结果是不一定的。我们把这种不确定造成结果行为叫做未定义行为。
知识点4:P36,2.1.3,转义序列
字符的转义序列可以为\后面加上最多3个8进制数字,或者\x后面加上多个16进制数字,那么当我们写"cout<<"\01234"<<endl;"时会发生什么呢,首先,\后面紧挨着的三位(012)中的12不会被当作8进制数,会被当成十进制的12,而剩下的无论是什么都会被当作普通的字符。这里因为012是\后面的数字,所以会被当作8进制数12,对应的10进制的ASCII码表是换行符。因此输出为\n34。"\012,34"和"\12,34"是等价的。当然,超出字符范围的数字,也就无法被转义了。
知识点5:P55,2.4.1,const的引用和指针
const是用来声明常量的标识符,代表我们不能够使用const后声明的变量名更改变量的值。如有前提const int i=32;,则之后给i赋值的语句就都是错误的。但是在int a=0;const int &p=a;的前提下,给a赋值的语句却是正确的,因为a不被const修饰而声明,这仅仅意味着,我们不能够通过p这个名称更改p的实体——a的值。由此可见,我们允许把引用绑定到const对象上,但是必须用const int &i=ci;这种形式,其中ci可以是也可以不是一个const对象,但是这个绑定操作之后i的值是不可更改的。
对一个常量的访问也可以用指向常量的引用或者指向常量的指针实现,不同的是,指向常量的引用是常量的一个别名,不能够被赋值,不过指向常量的指针本身是一个对象,它的本身值可以更改,不过指向的对象的值不能够被更改。
如果不想让指针指向的对象被更改,可以用const指针,int a=1;int *const p=a;这样指针本身的值不可以被改变但是它指向的对象的值是可以被改变的。如const int *const p=a;这种语句使指针本身和它指向的对象值都不会被改变。
知识点6:P57,2.4.3,顶层const
顶层const是对const而言的,“顶层”可以用来修饰const状态的形容词。一个const使对象本身的值固定,这个const就被称为顶层const,一个const是对象指向或引用的对象成为固定值,这个const就被称为底层const。顶层和底层const对拷贝来说密切相关,有相同底层const资格的两个对象才能够互相拷贝,而且顶层const声明变量之后不允许再次改变const的值。
像int v1=9;const int *p=&v1;int *p2=p;这种语句如果能够通过编译,那么我们就可以使用p2的性质改变p1指向的常量的值,但是常量的值是不能够被改变的,因此这种变相改变常量的值的表达式都是错误的。可以通过分析const级别得到表达式中常量是否被更改,从而判断语句的正确性。
说到底,顶层底层说的是对拷贝控制的约束。总的规则就是“不能改变常量的值”。因此“拷入和拷出的对象都要有相同的底层const资格,或者两个对象数据类型必须能转换”,例如,有int *p1,const int *p2;。p1没有底层const,p2有底层const。p1=p2;这时const int*不能转换成int *(如果转换,就违反了“不能改变常量的值这一约束条件”),因此p1=p2;不合法。p2=p1;int * 能够转换成const int *,因此p2=p1合法。
知识点7:P58,2.4.4,constexpr
我们在了解constexpr之前,应该先了解常量表达式。所谓常量就是固定的量,那么常量表达式就是值固定不变的表达式,这里“值固定不变”,指的是程序编译阶段,常量表达式的值就能被确定下来之后也不能对其进行任何种方式的修改。因此这个固定,是编译之后固定的。像cout<<1234<<endl;中的1234,就是常量表达式,显然,字面值是常量表达式。
constexpr的作用之一就是帮助程序员在IDE的提示下查看一个赋值语句是不是常量表达式。使用的方式包含在声明语句里面,形如constexpr 变量类型 变量名=右值;如果右值是一个常量,这条语句就是正确的。在所有函数体外声明的全局变量的地址就符合“在编译期间能确定,编译后值不被改变”这两个条件,因此也属于常量。
另外,用constexpr声明的指针(比如,constexpr int *p=&v1;中的*p,相当于int *const p=&v1; )都是顶层const,即指针本身值固定。
知识点8:P61,2.5.2,auto和decltype类型声明/指示符
auto变量通过初始化语句,计算出右值的类型,并推导出左值的类型。这个过程中auto将会忽视顶层const和引用类型,可用const auto &a=i;这种方式显式地指出了:指出要推导的结果是带顶层指针属性的或者是引用属性的。auto推导多个值时,这些值的类型必须是一样的。
decltype不通过计算,只通过推算出变量应有的值,表达式本身应有的值和函数的返回值来推导类型。对于变量类型,decltype保留顶层const和引用的属性。对于表达式,解引用表达式(如:int i=1; int *p=&i; decltype (*p) a=i;中的*p,对p解引用是int &类型的)和带括号的表达式,(如:decltype ((a+1)) c=i;)的结果都将是引用类型。
标签:
原文地址:http://www.cnblogs.com/bugrabbit/p/5092681.html