C++primer(第四版)温习笔记—第一篇:基本语言

C++primer(第四版)温习笔记—第一篇:基本语言

C++primer(第四版)复习笔记—第一篇:基本语言

再次阅读primer一方面是为了查漏补缺,另一方面也是更加深入的理解C++的思想精髓。在此记录复习中记录的各知识细节及理解,以便后续温故之用。

第一张:快速入门
1. for语句: for(初始化语句;条件测试语句;条件修改表达式){ 语句体 } 。执行顺序:初始化语句在最开始执行一次,然后执行条件测试语句,若成立则执行语句体;然后再执行条件修改表达式,再执行条件测试语句,成立则继续,反之则退出for语句。
2. if else语句缩进:

if(0==i){
        cout<<"i==0"<<endl;
    }else{
        cout<<"i!=0"<<end;
    }

3、读入未知数目的输入:

int value; 
while{std::cin >> value}
     sum+=value;//将输入的所有值相加(个数未知)。

while{std::cin >> value}语句中std::cin>>value返回输入流本身,while通过测试该流的状态来判断条件是否成立。如果状态有效则测试成功。输入流当遇到文件结束符或无效输入(此处比如输入的不是一个整数)时,则流对象的状态时无效的。
4、而文件结束符:Windows下按CTRL+Z,Unix下按Ctrl+D。
5、cin:用于从标准输入中读入的istream对象(cin>>value:从标准输入中通过istream对象cin,将输入读入到标量value)
Cout:用于写入到标准输出的ostream对象(cout>>value:将变量值通过osteam对象out输出到标准输出)
标准输入:和程序执行窗口相关联的输入流,这种关联由操作系统设定
标准输出:和程序执行窗口相关联的输出流,这种关联由操作系统设定。
6、函数实参:被传递给调用函数的值。 函数形参:函数定义的组成部分,指明可用什么参数来调用该函数。

第2章: 变量和基本类型
1、计算机将存储器中的每一个字节和一个地址关联。可以用地址表示从该地址开始的任何几个不同大小的位集合,比如可以说地址为0x….的字(4个字节)或地址为0x的字节。要让某个字节(8个bit位)具有意义,必须要知道存储在该地址上的值的类型。(该类型由额外的4个字节的系统开销来指定),从而根据该地址内容的类型来解释其中的数据,这才使得该地址的内容具有实际意义(否则只是由0、1组成的位串)。
2、BOOL与bool FALSE/TRUE与false/true的区别:
BOOL是通过#define BOOL int 得到的,因此BOOL为整型,同样FALSE/TRUE也是#define得到的整型,其大小与具体编译环境有关(整型一般为4字节大小,具体看环境)。其取值:BOOL是微软定义的typedef int BOOL(在windef.h中)。它是一个三值逻辑,TRUE/FALSE/ERROR,返回值为大于0的整数时为TRUE,返回值为0时候,为FALSE,返回值为-1时为ERROR。
而bool则是C++中新增的数据类型(C中没有),大小为一个字节,取值为true或false,大小为一个字节。bool取值false和true,是0和1的区别
3、有符号整型:以一个字节的来说明,取值为-8—7:正数,即除了最高位的符号位(为0)低三位的值(0—7);负数(最高位为1)时,转换为补码表示(正数的原码 反码 补码都一样,负数的反码为原码除最高位符号位外其他的取反,而补码为反码加1),因此1000,即表示-8:(反码为1111,补码为1111+1=符号位为1,此外还剩1111,即为8,因此结果为-8);同理1001为-7,1111为-1。
计算补码: 变量A用N位来存储(N不算符号位),当其为负数时计算其补码的方法是A=-(A+2^N);
4、整型的赋值:当把一个超出其范围的值赋值一个变量时,编译器必须调整时期满足要求——将该值对该unsigned类型的可能取值的数目求模,然后求得其值。如将336赋值给unsigned char(一个字节),结果为336%256=80; 在C++中将一个负值赋给unsigned 类型是合法的,其结果也是取模。如将-1赋值给8位的unsigned cahr 结果为:-1%256=255;(模的符号与除数一致,求余结果与被除数一致)
5、取模:任何一个整数a都可以表示成a=k*b+r,归纳:当a和b符号一致时,求模运算和求余运算所得的c的值一致,因此结果一致 ; 当符号不一致时,结果不一样。求模运算结果的符号和b一致,求余运算结果的符号和a一致。(-7 mod 4=1,-7%4=-3)。
6、为了与C语言兼容,C++的所有字符串字面值都由编译器自动在末尾添加一个空字符。
7、变量提供了有名字的存储区,因此可用变量名来引用对应存储空间的值。变量名只能有字母、下划线、数字组成,不能以数字开头。
左值:可以出现在赋值语句的左边或右边,如变量
右值:只能出现在赋值语句的右边,如字面常量:5 、”abc”。
8、两种初始化方式:复制初始化(int i=10;),直接初始化(int i(10);)。直接初始化效率更高(但对内置类型基本无区别)。
9、当没有指定初始化值时,系统有时会自动初始化。
内置类型自动初始化:在函数体外定义的变量都初始化为0(进程空间中未初始化数据段的所有变量,如全局未指定值的变量、全局和局部未指定值得静态变量,都自动初始化为0或者NULL指针),函数体内未指定初始值的变量(栈中的数据 如自动变量不进行自动初始化,其值未定义,可能为随机值:残留在所在内存地址中的数据值)不进行自动初始化。
类类型的变量的初始化:对于有默认构造函数的类型,可以不显示初始化;而对于没有默认构造函数的类型,必须提供显示的初始化。
10、变量定义:用于为变量分配存储空间,还可以指定初始值。在一个程序中变量有且有一个定义。定义包含声明。
变量声明:用于表明变量类型和名称,不是定义,也不分配空间。用关键字extern int I ;对i进行声明。注意包含初始化式的声明认为是定义(在此之前没有定义该变量,且在此之后不能再定义改变了) extern int i=10;被认为是定义。
11、const:定义非字面值常量:const int bufsize=512; bufsize为仍然为左值变量,但是其值不能再被修改。定义是必须初始化。 Const对象默认为文件局部变量,即在一个文件中定义的const变量在其他文件中是不能使用的,除非定义时申明为全局变量:(file1.cc)extern const int bufsize =512//定义并声明为全局变量;在其他文件中声明(file2.cc)extern const int bufszie;//声明。
12、引用:引用是定义另一变量的别名。(只能是对变量定义别名,不能是字面值 如 int &val=10;//错误 int i; int & val=I;//对,但const引用可以:const int &ref=512;对的),因为引用只是一已有变量的别名,因此引用不会给引用额外分配空间。
13、const 引用:const int val=1024; const int & refval=val;(int & refval2=val;//错误)(对const变量,必须绑定到const 引用上,而不能绑定到非const引用,否则就允许通过引用来改变原const变量值,这是不合法的)。const 引用是指向const对象的引用。 可以对非const变量定义const的引用,此时只能通过变量名修改其值(当然此时const引用的值也变量了!),而不能通过引用名来修改。
14、typefef:定义已有类型的同义词编译时处理,并没有引入新的类型。Typedef int INT;(原名在前);#define则是宏定义,其实是替代,在预编译时简单替代,不进行正确性检查。

#define int_ptr int *  (源在后)
int_ptr a, b; //相当于int * a, b; 只是简单的宏替换
typedef int* int_ptr;  (源在前)
int_ptr a, b; //a, b 都为指向int的指针,typedef为int* 引入了一个                    新的助记符

15、枚举类型(整数常量集)enum:enum Point{ a, b ,c};(a默认赋值为0,其后递增1)//定义了新的类型Point; Point p1=a;//定义Point类型的变量(注意枚举类型变量只能用其类型的枚举成员来赋值,或者用同类型的枚举变量赋值,而不能用其他任何方式赋值),(枚举成员a b c本身是一整形常量,可用于需要常量表达式的任意地方,如数组定义的维数 :int array[c];//则定义了一个2个元素的数组,const定义的整形也可作为数组长度)
16.类: 类定义数据成员时不能直接初始化,需要用构造函数初始化数据成员。 Private的数据成员只能通过其成员函数来访问,public的数据成员可以通过对象名访问。
17、calss与struct的区别仅在于默认访问级别不同。
18、头文件用于声明而不是定义:因为头文件包含在多个源文件中,所以头文件不应包含变量或函数的定义(例外:头文件可以定义类 const对象和inline函数,因为编译器需要他们的定义来产生代码)(注意为使const变量具有所有文件全局可用,定义是需要加上extern关键字)
19、头文件避免多重包含:#ifnfef AAA #define AAA #endif

第3章:标准库类型
标准库String类型:
1、 从标准输入读取sting时,读取并忽略开始的所有空白字符(空格 换行 制表符);读取字符指导在此遇到空白字符,读取终止。cin>>str;输入:“ Hello World! ”,则str实际是”Hello”,开头的空白被忽略,后面的空白已终止。
2、 String IO操作:getline(istream cin,string str);该函数从输入流中读取一行,并保存到sting中,只要遇到换行符就停止并返回(如果第一个字符就是换行符,则string对象为空,注意与cin读取一个string时的区别),返回值为流对象如cin,当while(getline(cin,str));作为条件语句时,与前面的cin>>str一样,测试返回的流对象状态。 (CTRL+Z结束输入)
3、 将一个字符串字面常量赋值给一个string对象时,不包含字符串中末尾自动添加的空字符,如string str=”abc”;则str只包含abc三个字符。
4、 不可用int代替strng::size_type类型(strng::size_type定义为无符号整型,因此其长度是int的两倍,如果用int来接受size_type的值,可能会造成溢出:int i=str.size();//可能返回的size_type值大于int的最大值而出错(正确:size_type size_t=str.size();)。
5、 String对象的赋值:string str1,str2=”aaa”; str1=str2;//(给str1赋值:先释放掉str1的内存,然后再给str1分配足够存放str2副本的空间,再将str2的字符复制到str1的空间),因此这种赋值方式是低效的。
6、 String和子面值字符串的连接: string str; str=”aa”+”bb”;//错误:连接+号的左右操作数至少有一个必须为string类型。
7、 String中单个字符的处理函数:头文件< cctype >中包含了大量处理单个字符的函数:例如toupper(str[2]);//将字符st[2]转化为大写字母,并返回大写字母; ispunct(str[2]);//测试字符是否为标点。等等!

标准库vector:
1、 下标操作符[],仅能对已经存在的元素进行操作。 该操作符返回对应元素的引用,因此vector vec(10,2); vec[2]=100;//使得vec的第三个元素为100,。对sting一样。
2、 所有的标准库容器都定义了相应的迭代器,而只有少数定义了下标操作符。
3、 注意迭代器失效问题。任何改变vector长度的操作(如push_back)都会使原来的迭代器失效。
4、 Const_iterator迭代器:vector::const_iterator const_iter;//不能通过该迭代器修改其指定元素的值。(但可以改变其指向的元素)
5、 两个同类型的迭代器相减得到其差值(类型为difference_type,被vector定义为signed的别名,类似于size_type为unsigned)。但是不能对两个迭代器相加,(两个指针相加,未定义)。

标准库bitset类型
C++primer(第四版)温习笔记—第一篇:基本语言
C++primer(第四版)温习笔记—第一篇:基本语言

第四章:数组和指针
数组:
1、 数组维数必须为大等于1的常量变表达式(字面常值、枚举常量、const常量:const常量+字面常量,还是常量)。
2、 当用字面值字符串来初始化字符数组,不能忘了给字符串末尾的NULL预留空间。
3、 不允许对数组直接复制和赋值:int a[]={1,2,3}; int b[3]=a; //错误(原因在于:数组名是不可修改的左值:常量指针)
4、 取地址操作符只能用于左值
5、 C++使用将一个表示符声明为一个指针。风格:int a; 与 int a;都对,但应使用后者,如:int a, b;//实际上只有a为指针,b为int。(会误解为a b都为指针); 而int *a,*b;//才定义了两个指针。
6、 指针值为0,表明指针不指向任何对象,避免使用未初始化指针。如果定义指针时不能指定所指对象,则应将指针初始化为0.(只能把字面值0给指针,而不能把值为0的整型变量赋给指针)(预处理变量null为0)
7、 Void*指针能接收任何非const类型对象的地址,但是不提供所指地址上对象的类型信息,因此不能对void指针所指的对象进行直接操作,如解引用等。可做比较、函数参数等
8、 指针的算数操作:当指针指向数组中的某个元素时,对该指针进行加减n操作,得到一个新的指针,该新指针指向原元素的前面或后面的第n个元素。(注意:加减n不能使得新指针越界。 允许指向数组最后一个元素的下一个位置,即超尾,但不能对超尾进行解引用,而只能作为哨兵,作用与vector的end()迭代器一样)。
9、 指向同一数组中元素的两指针相减(包括超尾指针): 得到两指针间的间距,类型为ptrdiff_t (signed)。 两个指针相加,是无意义的!!
10、 下标和指针: 使用下标访问数组实质是由下标+原指针得到一个指向新元素位置的 新指针。 如:int a[10]={}; a[5]即为首指针a加上5得到指向第6个元素的新指针; 而int* p=&(a[5]); p[-2];//由于p初始指向第6个元素,然后又后退一2个,因此p[-2]及为a[3];
11、 数组名指向首元素的常量指针: 不能对其进行++,–等操作来改变其指到其他元素上去。
12、 数组末尾的下一个单位的指针为数组的超尾: 超尾起哨兵作用,用于比较,防止越界。但不能对其解引用。 不能计算超尾以后和首地址以前的地址。
13、 (指针常量)指针const对象的指针:const int *p;(可以不初始化) 理解为指向常量。 即所在对象的值不能改变,但可以改变指针指到其他对象。可以给指针常量赋非const对象的地址(此时仍不能通过该指针常量来改变其值,因为始终假设其指向的对象为const对象)。
14、 (常量指针)cosnt指针: int *const p=&a;//必须初始化。指针本身为常量,不能改变该指针使其指向其他的对象。 但所指对象的值是否改变,取决于对象本身是否为const对象。
15、 指向const对象的const指针:const int *const p=&a;//必须初始化。所指对象和所指对象的值都不能改变。
16、 Const修饰紧接其后的标识符。
17、 字符串字面值的类型就是const char 数组。时刻注意末尾的NULL
18、 处理C风格字符串的标准函数库(string.h的c++版本)cstring。

19、 初始化new动态分配的数组:如果元素类型为类类型,则默认构造函数初始化。 若为内置类型,则无初始化值。

String *str=new string[10];//10个元素都初始化为空
Int *p=new int[10];//10个元素都未初始化
Int *p1=new int[10]();//在后面跟上括号,要求编译器将其元素都初始化为0.

20、 String 和C风格的字符串混用:
(1)、可以用C风格字符串对string进行初始化和赋值(忽略末尾的NULL)。
(2)、反之不成立:不能用string直接对C风格字符串进行初始化或赋值。可用string的成员函数c_str将string转为C风格字符串。注意:c_str()函数返回一个指针常量,该指针指向string的字符串(在末尾加NULL)。
例:string str(“aaa”); const char *c=str.c_str();//此时c指针指向“aaa”(末尾有NULL) 然后修改str:str=”bbb”; 则c的值也被改变为“bbb”末尾加NULL。
21、 多维数组:理解为数组的数组。
(1)、int *p[4];//定义指针数组:p为包含4个指针的数组。
(2)、int (*p)[4];//p为一个指向有4个元素的数组的指针。

第五章:表达式
1、 %求余或取模。其操作数只能为整型。 两整数相除,结果为整数,小数被截去。
2、 两个表达式求与或运算,仅当第一个表达式不能确定整个结果为false或、true时才需要计算第二个表达式的值,或者不会计算第二个的值。
3、 注意string也有迭代器:string::iterator it=s.end();
4、 不应串接使用关系操作符: 关系操作符(< <= > >=)具左结合性: if(i

void func(int  &a[10]);//元素为引用的数组
void func(int  (&a)[10]);//这才是数组的引用,括号不可少
类似: 
int *a[10];//表示10个指针的数组
Int  (*a)[10];//表示数组的指针;括号不可少

可理解为:*和&向前结合。
3、 多维数组即数组的数组: 多维数组形参除了第一位的长度可不指定(与一维一样,编译器自动忽略),其他维长度必须明确指定。 实际传入的时一个指向数组的指针。
4、 返回引用、返回常引用。 千万不可返回局部对象的引用或指针。
5、 函数声明:返回类型、函数名、参数列表 ,这三个元素称为函数原型,描述了函数接口。
6、 函数默认实参:在声明或定义时,如果个其中一个参数指定了默认实参,则其后的所有参数都要指定默认实参; 相反,在调用时若给其中一个参数传入了实参,则该参数之前的所有都应该显示传入实参,默认实参值能填补末尾空缺的形参。 既可以在函数声明时也可以在函数定义时指定函数默认实参,但是不同同时指定。 通常在声明中指定。
7、 内联函数:避免函数调用的开销,但这只是提供给编译器的建议。 内联函数必须放在头文件中定义。
8、 类的成员函数:函数体既可以在类中定义,也可以在类外定义。编译器隐式的将在类内部定义的成员函数当做内联函数。
9、 每个成员函数隐含一个额外的参数:this指针,即指向调用该函数的对象的指针。比如在该函数中使用其对象的其他成员变量等都是通过该隐含参数来调用的。
10、 常量成员函数:即在函数的声明的形参表后加上关键字 const。这使得调用该成员函数的对象为const对象,即this指针为指针常量,指向const对象,因此在该成员函数中不能改变对象的成员。注意:声明与定义必须一致,声明时有const,则定义时也必须有。
11、 注意:const对象、指针向const对象的指针或引用,只能用于调用const成员函数,如果尝试调用非const成员函数则是错误的。(因为他们表示不修改对象的任何成员,如果调用非const成员函数则可能修改对象)
12、 This指针:在成员函数的形参表中不能显示的包含this指针参数,在函数体中任何没有前缀的使用其对象的成员被假定是通过this指针调用的。 (在函数体中可以显示的使用this指针来调用自身对象的成员,是合法的:this->member;)
13、 构造函数:构造函数放在public部分,没有返回值,可带初始化列表(参数列表与函数体的大括号之间,以:开头)
(1)、默认构造函数:指明没有显示提供初始化时该怎么办默认调用,不用提供任何初值。(如果没有为类显示定义任何构造函数,编译器会自动生成一个默认构造函数,这种构造函数一般只适用于仅包含类类型成员的类,含有内置类型成员的类,应该自己定义默认构造函数)
(2)、构造函数的初始化列表:为类的一个或多个数据成员指定初值。如类A: 构造函数:A():intmemb(0),doubmemb(0.0){ };//没有给定初值的数据成员,调用其自身的默认构造函数进行初始化。
14、 函数重载:在同一作用域中的多个函数,具有相同的函数名,而形参表不同,则成为重载函数(main函数不可重载)(注意内部作用域的名称会覆盖外部作用域的函数或变量)
15、 函数不能仅仅基于返回类型的区别来重载(如果两个函数的参数列表相同,只是返回类型不同,则第二个函数声明是错误的)
16、 选择确定重载函数的三个步骤: 寻找候选函数集、选择所有可行函数(包括精确匹配的、类型转换匹配的)、寻找最佳匹配。(如果确定不出最佳匹配的一个,则调用具有二义性,出错)(类型提升 优于 标准转换
17、 仅当形参为引用或指针时,形参是否为const才有影响,此时才可以通过是否为const来重载。(如参数:引用 与常引用 可用来重载; 非引用 和const非引用 不可以,因为两者都是实参的拷贝,实参均可为const或非const) 对指针同理:(f(const int )与f(int )是重载;; f(int *)与f(int *const );不是重载,二者都可接受常量或非常量指针,都为拷贝)
18、 函数指针: 和其他指针一样,函数指针也某个特定的类型,函数类型由其返回类型和参数列表确定,与函数名无关。
19、 返回指向函数的指针:int (func(int))(int ,int);
由内到外理解: 内:func(int):func是一个带有一个int参数的函数。 该函数的返回值(外层)为: int()(int, int);这样的一个函数指针。
可通过typedef来使得简单易懂:
Typedef int (pf) (int , int);//定义了一个函数指向带两个参数(一个整型指
//针,一个整型)且返回值为int的 类型 的函数指针。
Pf ff(int);//ff为参数为int,返回值为pf这种类型的函数。

第八章:标准IO流 (标准输入输出流 文件流 字符串流)
1、 IO标准对象不可复制或赋值: 当需要传递IO对象时必须传递io的引用或指针。 由于对IO流对象的读写会改变其状态,因此不能传递const的引用。
2、 输出缓冲区的管理:一下几种情况将导致输出缓冲区被刷新,即写入到真是的输出设备或文件:
(1) 程序正常结束:main返回工作的一部分,将清空所有的缓冲区。
(2) 在一些不确定的时候,缓冲区已满
(3) 显示调用刷新缓冲区的操作符:enl flush ends 等。
3、 需要注意: 程序崩溃时,不会刷新缓冲区,因此在根据输出来调试程序时,需要确保每次输出的都成功的刷新输出到设备上。 因此在每次输出时都使用endl来刷新输出。
4、 当输入与输出流关联在一起时,任何读输入流的尝试都将首先刷新输出缓冲区。标准库将cin cout关联在一起。 Cin>>ival;导致cout关联的缓冲区被刷新。 可用函数tie来将输出流 输入流关联。
5、 文件流对象的使用:(需要自己定义两个文件输入输出流对象)头文

#include <fstream>
ifstream ifile;//文件输入流对象(没关联到文件)
//输出流,初始化关联到给定路径文件(C风格)
ofstream ofile("C:Desktop\\test(2).txt");        
ifile.open("C:Desktop\\test(2).txt");//关联
ofile<<"where are you? "<<endl;//向文件写数据 
string str;   
ifile>>str;//从文件读数据:与cin一样,遇到空白止
getline(ifile,str);//从文件输入流关联的文件读入一行到str
cout<<str;   //输出到标准输出设备

注意:在open打开文件后,要记得检查是否打开成功
if(!ifle)//若不成功则提示。 另外,fstream一旦打开,就保持与指定文件关联,若要把文件流对象关联到其他文件,则必须先关闭close当前文件,然后再open其他文件,且在再次open之前需要调用clear函数清除流状态标识。
6、 Open打开文件时注意设置打开模式。如要用ofstream打开文件时保留已有内容,则必须显示的将文件设置为app打开模式。 Ofile(“filename”,ofstrream::app);
7、 Fstream流对象,既可以读也可以写打开的文件。
8、 字符串流: 将流对象sstream(需要包含头文件sstream)与string对象绑定,从而通过sstream读写string字符串