C rimer 初学者可看笔记.docx
《C rimer 初学者可看笔记.docx》由会员分享,可在线阅读,更多相关《C rimer 初学者可看笔记.docx(14页珍藏版)》请在冰豆网上搜索。
Crimer初学者可看笔记
C++primer
第一章快速入门
学习了一个c++程序最基本的框架,和如何完成一个完整的程序编程。
了解已初始化和未初始化变量的区别。
初始化是一个很重要的概念,贯穿了c++始终。
在程序变的很复杂之前学会做好注释,在c++中使用//表示注释内容。
初步接触了while语句,for语句,if语句。
While语句提供了迭代执行功能。
[while(condition)while_body_statement];
For语句可以简化管理循环变量的代码。
[for(一个初始化语句,一个条件,一个表达式)];
If语句用来转折编程。
[if(condition){}else{}];
类机制是c++中最重要的特征之一,我们通过定义类来定义自己的数据结构。
需要注意的是标准库的头文件用尖括号<>括起来,而像自定义的类这种非标准库的头文件用双引号“”括起来。
类可以执行很多操作符,例如+=<<>>。
第二章变量和基本类型
(1)基本内置类型:
表示整数,字符,和布尔值的算术类型合称整型。
除了bool型外的整型又分带符号和无符号类型。
类型float,double和longdouble都是浮点型。
单精度浮点型只能保证6位有效数字,而双精度可以保证10位有效数字。
(2)字面值常量:
整型字面值规则,可以是使用十进制,八进制,十六进制。
浮点字面值规则可以用十进制或者是科学计数法来表示浮点字面值常量。
Bool字面值和字符字面值是truefalse。
字符串字面值常量用双引号括起来的零个或者多个字符表示。
多行字面值可以再一行的末尾家一反斜杠符号可将此行和下行当做一行处理。
(3)变量:
变量是提供了程序可以操作的有名字的存储区。
而变量名是变量的标识符。
可以由字母,数字和下划线组成。
变量名必须是由字母或者下划线开头的,并且区分大小写。
(4)const限定符:
定义const对象是把一个对象转化成一个常量。
因为常量在定义后不能被修改,所以定义是必须初始化。
(5)引用:
引用就是对象的另一个名字,是一种复合类型,通过在变量名前添加“&”符号来定义。
(6)typedef名字:
typedef可以用来定义类型的同义词。
一般使用typedef有三种目的,1.为了隐藏特定类型的实现,强调使用类型的目的。
2.简化复杂的类型定义,使其更易理解。
3.允许一种类型用于多个目的,同时使得每次使用该类型的目的明确。
(7)枚举:
enum枚举类型名{枚举成员1,枚举成员2…};(8)类类型:
c++中通过定义类来自定义数据类型。
类定义了该类型的对象包含的数据和该类型的对象可执行的操作。
定义了一关键字class开始,其后是该类的名字标识符。
类体位于花括号里面,花括号后,面必须要跟一个分号。
第三章标准库类型
1.命名空间的using声明:
使用using说明就可以在不需要加前缀namespace_name:
:
的情况下访问命名空间中的名字。
如果在头文件中放置using声明,就相当于在包含该头文件的每个程序中都放置了同一using声明,不论该程序是否需要using声明。
2.标准库string类型:
string类型支持长度可变的字符串,标准库string类型的目的就是满足对字符串的一般应用。
3.标准库vector类型:
vector是同一种类型的对象的集合,每个对象都有一个对应的整数索引值,我们一般把vector称作容器。
。
vector是一个类模板。
使用模板可以编写一个类定义或函数定义,而用于多个不同的数据类型。
4标准库bitset类型:
标准库提供的bitset类简化了位集的处理。
类似于vector,bitset类是一种类模板,而与vector不同的是bitset类型对象的区别仅在其长度而不在其类型。
第四章语句
1.if语句:
if语句根据特定表达式是否为真来有条件的执行另一语句。
If语句有两种形式,一种有带else分支,而另一种则没有。
2.switch语句:
switch语句提供了一种更方便的方法来实现深沉嵌套的if/else逻辑。
在switch语句中,漏写break语句是常见的程序错误。
但是break语句,也要慎用,并不是总是恰当的。
在switch语句中,default标号提供了相当于else子句的功能。
3.while语句:
当条件为真的时候,while语句反复执行目标语句。
再循环条件中定义的变量在每次的循环里面都要经历创建和撤销的过程。
4.for语句:
for语句的语法形式是:
for(init-statement;condition;expression)statement。
5.dowhile语句:
该语句保证了循环体至少执行一次。
dostatementwhile(condition);分号一定不能忘了。
6.break语句:
break语句用在结束最近的while,dowhile,for,switch语句,并将程序的执行权传递给紧接在被终止语句之后的语句。
7.continue语句:
continue语句导致最近的循环语句的当次迭代提前结束。
对于while和dowhile语句,继续求解循环条件。
而对于for循环,程序流程接着求解for语句头中的exp表达式。
8.goto语句:
由于所有goto的程序都可以改写成为不用goto语句,所以,也没有必要再使用goto语句了。
第五章函数
1.函数的定义:
所谓的函数就是由函数名以及一组操作数类型唯一的表示。
函数的操作数就是形参,在一对圆括号中声明,形参与形参之间以逗号隔离。
而所谓的函数调用就是使用调用操作符()实现函数的调用。
函数的调用其实就是做了两件事情:
用对应的实参初始化函数的形参,并将控制权转移给被调用的函数。
形参与实参的差别在于形参是在函数的形式表中定义的,并由调用函数是传递给函数的实参初始化。
2.定义函数的一般形式:
定义无参函数的一般形式:
类型标识符函数名(){声明部分语句}定义有参函数的一般形式:
类型标识符函数名(形式参数列表){声明部分语句}。
3.函数的参数和函数的值:
其实在定义函数时指定的形参,在未出现函数调用时,它们并不占内存中的存储单元,因此才称它们是形式参数或虚拟参数,表示它们并不是实际存在的数据,只有在函数发生调用时,函数中的形参才被分配内存单元,一遍接收从实参传来的数据。
在每一次的调用结束后,形参所占的内存单元也会被释放。
实参可以是常量,变量或者表达式。
而在定义函数时,必须在函数首部指定形参的类型,在函数的调用过程中,实参与形参的类型应相同或赋值兼容,实参变量对形参变量的数据传递是值传递,单向传递,只有实参传给形参,不能由形参传回给实参。
函数的返回值就是通过函数中的return语句获得,函数的类型决定返回值的类型。
4.函数的调用:
函数的调用方式一般用三种,一、函数语句。
二、函数表达式。
三、函数参数。
想要调用一个函数要具备以下的几个条件:
首先被调用的函数必须是已经存在的。
如果使用到库函数,记得在开头用#include包含进来。
如果使用用户自己定义的函数,而该函数与调用它的函数在同一程序单位中,且位置在主调函数之后,则必须在调用此函数之前对被调用的函数作声明。
5.内置函数:
这是c++提供的一种可以提高效率的方法,在编译时将所调用函数的代码直接嵌入主调函数中,而不是将流程转出去。
指定内置函数的方法只需在函数首行的左端加一个关键字inline即可。
内置函数虽然能节省运算时间,但是会增加目标程序的长度。
因此只将规模小的函数声明成内置函数。
6.函数的重载:
C++允许用同一个函数名定义多个函数,这些函数的参数个数和类型不同。
这就是函数的重载。
是一个函数名可以多用,有不同的含义。
7.函数的模板:
所谓的函数的模板,实际上就是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。
这个通用函数就是函数模板。
凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需要在模板中定义一次即可。
在调用函数系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。
定义函数模板的一般形式为template或template。
用函数模板比函数重载更方便,程序更简洁。
但是只适用于函数的参数个数相同而类型不同。
8.有默认参数的函数:
有时候在主函数调用别的被调函数时,使用同样的实参,因此可在定义函数的时候给形参一个默认值。
这样可以简化编程,提高运行效率。
一个函数不能既作为重载函数,又作为有默认参数的函数,因为很容易出现二义性。
9.函数的嵌套调用:
C++不允许对函数作嵌套定义,也就是说在一个函数中不能完整地包含另一个函数。
在一个程序中每一个函数的定义都是互相平行很独立的。
虽然布恩那个嵌套定义函数,但是可以嵌套调用函数。
10.函数的递归调用:
在调用一个函数的过程中又出现直接或间接地调用该函数本身。
这就是函数的递归调用。
11.局部变量和全局变量:
所谓的局部变量就是在一个函数内部定义的变量,只在本函数范围内有效,只能在本函数内才能使用。
全局变量是在函数之外定义的变量。
全局变量的有效范围为从定义变量的位置开始到本源文件结束。
12.变量的存储类型:
分为两种,静态和动态存储。
所谓的静态存储方式是指在程序运行期间,系统对变量分配固定的存储空间。
而动态存储方式则是在程序运行期间,系统对变量动态地分配存储空间。
前面提到的全局变量就是存放在静态存储区中,在程序执行的过程中它们占据固定的存储单元。
在动态存储区中存放的是函数形式参数,和函数中的自动变量,以及函数调用时的现场保护和返回地址等。
可以使用static将局部变量变为静态局部变量。
静态局部变量在静态存储区内分配存储单元。
在程序整个运行期间都不释放。
第六章数组与字符串
1.数组的定义:
数组是一组相同类型的数据的集合,他们占用连续的存储单元,数组中的每个数据称为一个元素。
2.一维数组定义:
数组的三要素是其名称,以及每个元素的数据类型,还有就是必须是常量表达式的数据个数。
在定义一个数组的时候必须要明确数组中的元素的个数,否则编译器会报错。
还有要注意的是在访问数组的时候,最大下表应该为数组元素个数减1,不然会导致程序的异常行为。
依然可以使用sizeof()来计算某个数组所包含的元素个数。
3.字符数组与字符串:
字符数组是指数组里面是存储字符数据,在c++中,字符串是一空字符(‘/0’)结尾的字符组,空字符是字符串结束的标志。
我们可以使用cin对象的getline来安全读取数据。
Getline可以读取包括空格的整行,也可以限制最多读取的字符数,或结束字符。
可以使用strcpy将字符串复制到另一个字符数组中,增加一个参数可以限制最多复制的字符数,防止写入的数据时越过数组边界。
使用strcmp来比较字符串的大小,使用strcat连接字符串,使用strlen来获得字符串的长度。
4.多维数组:
以为数组可看作是一行数据,二维数组对应由行列构成的数据网络,其中一维对应于行,另一维对应于列。
访问二维数组中的元素,需要用2个下标,下标都是从0开始的,c++中规定,先指定行下标,再指定列下标。
5.创建和使用指针变量:
学习指针之前首先要了解一下地址,地址单元的编号就是该单元的地址,它表明了内存单元在内存中的相对位置。
变量的类型不同,占用的内存大小也不同。
变量占用的首个内存单元的地址就是变量的地址。
在C++中可以定义特殊的变量,用来保存普通的变量地址,这就是指针变量。
用“*”放在变量前面说明变量是用来存储一个变量的地址,一般定义一个指针变量的时候要对它进行初始化,这是个良好的习惯。
我们通过&,=将一个变量的地址保存到指针变量之中。
称指针变量指向某个变量。
要特别注意的是,在指针赋值的时候,取地址的变量类型必须要与指针类型一样,否则编译器报错。
值得注意的是,在定义指针变量之后,指针变量前的*表示解除引用,表示“存储在…处的值”。
6.指针的使用:
一传递函数参数,实现对实参的间接修改。
二访问类中成员数据和成员函数。
三管理堆中的数据。
6.栈和堆:
每个程序运行时,会创建5个内存区域:
全局名称空间(全局变量),栈(保存局部变量和函数形参),堆(自由存储区,是预留给程序员的大块内存,程序员可以从堆中请求内存空间,使用后释放内存空间),寄存器(用于内部管理,CPU内部的临时数据,指令缓存,状态管理),代码空间(保存程序指令)。
7.栈和堆的使用:
使用new运算符可以从堆中分配内存。
堆中分配的内存,没有对应的变量,只能通过指针间接访问。
而使用delete运算符,可以释放请求的堆空间。
,使用完堆的内存区域后,必须调用delete释放相应空间,避免内存泄露。
在使用了delete指针之后,该指针将成为悬摆指针,解析或者再次delete该指针将会导致程序崩溃。
所以在delete指针后,应赋值指针NULL或者重新分配内存空间delete的本质只是释放指针所指向的堆空间,对指针本身并没有什么影响。
此时指针依然指向原先的内存位置。
第七章类的基础概念
1.类和对象的定义:
我们首先要知道的是抽象是对具体对象问题进行概括,抽出这一类对象的公共性质加以描述的过程。
将抽象出的数据成员,代码成员相结合,视为一个整体,通常我们称之为封装。
在函数中我们用{}括起来。
继承是c++中支持层次分类懂得一种机制,允许程序员在保持原有类特性的基础上,进行更具体的说明。
总的来说,类是具有相同属性和行为的一组对象的集合,它为属于该类的全部对象提供统一的抽象描述。
其内部包括属性和行为两个主要部分。
利用类我们可以实现数据的封装,隐藏,继承与派生。
类一般是用来编写大型的复杂程序,其模块化程度比c中采用函数更高。
类中的成员分为:
数据成员和成员函数。
一般在public后面的声明是类与外部的接口,任何外部函数都可以访问公有类型数据和函数。
而在private后面的申明,只允许本类中的函数访问,而类外部的任何函数都不能访问。
值得注意的一点是,如果在类中不写private和public的话,那么系统默认是private声明。
在类中定义的变量我们称为类的对象。
内联函数:
为了提高运行是的效率,对于较为简单的函数可以声明为内联形式,而内联函数不能由复杂的结构,比如循环结构等,在类中声明内联成员函数的方式是使用inline关键字或者是直接在类中直接定义函数。
2.使用对象:
定义完类后,在类中创建对象后,可以通过.操纵符来访问它的成员函数和成员变量,但是只能访问public部分的成员函数和成员变量。
(在定义一个类时候,class类名{};分号一定要记得),在类中定义的每个成员函数,都必须要进行定义。
类中成员函数的定义与普通函数类似,但需要制动类作用与前缀,表示的为特定某个类的成员函数。
3.构造函数和析构函数:
构造函数是类中的一种特殊的函数,用于创建对象时,完成对数据成员的初始化。
构造函数是由系统自动调用的,每当我们创建对象时,系统就会自动调用相应的构造函数。
也就是说,如果没有声明构造函数,编译器自动生成1个默认构造函数,但不执行任何操作。
如果声明了构造函数,往往也需要声明析构函数,析构函数用于在对象消亡时执行清理工作并释放配给对象的内存。
析构函数为类名之前加~,没有返回类型,不能传递任何参数,有且仅有一个。
当然析构函数也由系统自动调用,每当对象消亡的时候,系统就会自动调用相应的析构函数。
4.const成员函数与常对象
5.内联函数:
将普通函数声明为内联函数,可以提高运行效率,类的成员函数也可以声明为内联。
6.对象成员:
静态成员:
同一个类不同对象的数据成员所占用的内存空间是不同的。
在一些情况下,类的数据成员的值对每个对象都是相同的,如当前已创建对象的数量,这是可以将该数据成员声明为静态数据成员。
静态成员的初始化放在类定义的外部。
静态成员又分为静态数据成员和静态成员函数。
,静态数据成员具有全局性,供类的所有对象共享。
静态成员函数只能访问类的静态成员,而不能访问类的非静态成员。
因为当通过类名和运算符:
:
调用一个静态成员时,不能确定函数中所访问的非静态成员属于哪个对象。
7.类地友元:
友元函数实在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问private和protect成员,能增加灵活性,使程序员可以再封装和快速性方面做合理选择。
访问对象中的成员必须通过对象名。
若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员,一样用friend关键字,要注意的是友元类的关系是单向的,若B是A类的友元,只能B访问A的私有和保护数据。
反之不行。
8.const成员函数与常对象:
将类中某些只会读取数据成员的函数声明为常成员函数,只要在函数头的结尾加上const,因此在常成员函数中不能修改数据成员,否则编译器将报错。
要养成好的编程习惯就要尽可能的将成员函数声明为常成员函数,这样可以防止对数据成员的意外修改。
常对象:
常类型的对象必须进行初始化,而且不能被更新。
需要注意的是通过常对象只能调用它的常成员函数。
常引用:
在声明引用时用const修饰,被声明的引用就是常引用,常引用所引用的对象也是不能被更新。
9.类中的指针应用:
类似在堆中创建一个普通类型数据空间,也可以在堆中创建一个类的对象。
用完之后用delete释放对象,将调用对象的析构函数,然后释放堆中的内存。
访问堆中创建对象的方法,只能通过指针间接访问,两种访问方式:
(*对象名).成员函数名;对象名->成员函数名。
指针数据成员:
类中包含的指针数据成员,每个指针指向堆中的对象。
如果类中含有指针数据成员,就要编写析构函数来释放内存,以免造成内存泄露。
第八章继承与多态
1.继承的概览:
一个新类可以从现有的类继承特征,特征表示属性和方法,而从现有的类产生新类的过程称为派生。
一般我们称现有的用来派生新类的类称为基类或父类,而派生出来的类称为派生类或者子类,当然派生类可以作为基类继续来派生类,从而形成类的层次结构。
派生类的定义是class派生类名:
继承方式基类名{private:
protected:
public:
}。
2.继承方式:
继承方式分为三种,分别是public:
公有继承;private:
私有继承;protected:
保护继承。
通常我们在派生类类体中添加数据成员以及成员函数,而基类的成员将自动的称为派生类的成员,不用再去编写。
派生类不能直接访问基类的私有成员。
公有继承(public):
特点就是基类的公有成员和保护成员作为派生类的成员时,保持原有状态,而基类的私有成员还是私有,不能被派生类访问。
私有继承(private):
私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。
保护继承(protected):
其特点就是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数,或者友元访问,而基类的私有成员依然是私有的。
派生类中除了拥有新定义的成员外,派生类还拥有基类的所有成员(当然除了基类的构造函数和赋值操作符重载函数除外)。
在定义派生类的时候一定要见到基类的定义。
派生类与友元的关系:
在派生类中没有显式说明,基类的友元不是派生类的友元,如果基类是另一个类的友元,而该类没有显式说明,则派生类也不是该类的友元。
封装与继承的矛盾:
在派生类中定义新的成员的时候,往往需要用到基类的一些private成员,于是这时候就需要用到protected,protected的成员不能被对象使用,但是可以在派生类中使用。
Protected访问控制缓解了疯转与继承的矛盾。
派生类成员标识符的作用域:
派生类成员名的作用域嵌套在基类作用域中,如果派生类中定义了与基类同名的成员,则基类同名成员时要用基类名加标识符。
3.构造与析构:
派生类的对象的数据结构是有基类中说明的数据成员和派生类中说明的数据成员共同构成。
将派生类的对象中由基类中说明的数据成员和操作所构成的封装体称为基类子对象,它由基类中的构造函数进行初始化。
由于构造函数不能够被继承,因此派生类的构造函数必须通过调用基类的构造函数来初始化基类子对象。
所以在定义派生类的构造函数是除了对自己的数据成员进行初始化外,还必须负责调用基类构造函数使基类数据成员得以初始化。
如果派生类中还有子对象是,还应包含对子对象初始化的构造函数。
当对象被删除是,派生类的析构函数被执行。
由于析构函数也不能被继承,因此在执行派生类的析构函数是,基类的析构函数也将被调用。
在派生类中是否定义析构函数与基类无关。
若派生类对象在退出其作用域前,有数据需要做善后工作,就需要定义析构函数,基类的析构函数不会因派生类没有析构函数而得不到执行,他们各自是独立的。
若基类,成员类,派生类都有析构函数,则执行的顺序是:
先自己(派生类),再客人(成员对象),后祖先(基类)。
其顺序与执行构造函数的顺序正好是相反的。
派生类构造函数在积累中有缺省的构造函数的情况下,或者是根本没有定义构造函数时可以省略对基类构造函数的调用。
若基类构造函数有参数,则派生类必须定义构造函数,提供将参数传递给基类构造函数。
4.多重继承:
多继承可以看做是单继承的扩展。
所谓多继承是指派生类具有多个基类,派生类与每个基类之间的关系仍可以看作是一个单继承。
多继承的继承方式及访问控制的规定同但继承,派生类拥有所有基类的所有成员。
多继承下派生类的构造函数与单继承下派生类构造函数相似,它必须同时负责该派生类所有基类构造函数的调用。
同时,派生类的参数个数必须包含完成所有基类初始化所需要的参数个数。
派生类构造函数执行的顺序是先执行所有的基类的构造函数,然后在执行派生类本身构造函数,处于同一层次的个基类构造函数的执行顺序取决于定义派生类是所指定的个基类顺序,与派生类构造函数中所定义的成员初始化列表的各项顺序其实是无关的的。
,所以由此可见,派生类构造函数的成员初始化列表中各项顺序可以任意的排列。
由于是多继承的情况下,所以可能造成对基类中某成员的访问出现了不唯一的情况,因此产生二义性,所以需要使用作用域运算符:
:
来进行限定。
:
:
作用域运算符也可以用来限定一些不同基类中的同名成员函数。
重复继承--虚基类构造函数:
C++中规定,虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化,如果一个派生类有一个直接或者间接地虚基类,那么派生类的构造函数的成员初始列表中必须列出对虚基类构造函数的调用,如果没有列出,则表示使用该虚基类的缺省构造函数来初始化派生类对象中的虚基类子对象。
而且在一个成员初始化列表中出现对虚基类和非虚基类构造函数的调用,则虚基类的构造函数先于非虚基类的构造函数的执行。
5.多态概述:
多态指不同对象接收到相同消息时,产生不同的行为(调用不同的方法)。
其实就是使用同一个函数名,调用不同内容的函数,实现“一个接口,多种方法”。
我们都知道在C++中通过覆盖,运算符重载,虚函数等技术,使得基类和派生类中可以出现同名的成员函数。
不同的成员函数被调用的时候表现出的不同的行为,表现出很强的灵活性。
静态多态性:
编译时的多态性,成员函数重载,覆盖,运算符重载都属于静态多态性。
编译器根据实参数据类型或对象的数据类型,在编译时就确定调用哪个函数。
动态多态性:
运算时多态性,通过虚函数来实现。
通过虚函数实现的动态多态性,在代码执行的过程中决定调用哪个函数。
重载:
同一个类中,存在名称相同但“签名不同”的成员函数,其实就是其参数类型或者类型不同罢了,编译的时候根据实参类型确定调用的是哪个版本的函数。
覆盖:
派生类和基类存在名称相同的成员函数,实现派生类方法覆盖基类方法的功能。
若要访问基类被覆盖的函数,要使用类名前缀。
赋值兼容性规则:
每一个派生类的对象,都是基类的一个对象。
赋值兼容规则是指在公有派生情况下,一个公有派生类的对象可以当做基类的对象使用,反之是禁止的。
6.虚函数与运行