目录
第一章 C++概述
①编辑-编译-链接-运行
②概念:类、封装、对象、消息、数据成员、成员函数
③三大特性:封装性、继承性、多态性
④继承性优点:避免公共代码的重复编写,减少代码和数据的冗余
⑤多态性的意义:在于同一个接口实现不同的操作
(静态联编,动态联编)
⑥面向对象的语言:Ada\Eiffel\Object-C\Simula 67(鼻祖)\python
⑦类与对象在内存中均占有内存单元×
第二章 C++对C语言的改进及其扩展
1.C++源程序的基本单位:函数 (输出:任何程序都必须有输出,输入不一定)(std是标准名字空间名称,文件iostream来自于该 名字空间)
2.C++中,数据的输入和输出是通过I/O流实现的。cin和cout是系统预定义的流类对象 >>
3."提取符":>> 后面只能跟变量 默认分隔符 :空格 tab 回车
4.新增bool类型: boolalpha和noboolalpha操纵符
5.两种注释方式:// (别搞成了转义字符) 和 /* … */ 前者可嵌套,后者可嵌套前者(但不能嵌套自身
6.名字空间namespace的定义和使用:①右边大括号后面不用分号(区分class)
②大括号内可以出现任意实体的声明or定义
③三种使用名字空间中的内容(作用域分辨符)
④C++允许自己定义并使用名字空间,以防止命 名冲突
7.新增string类型:①头文件<string>
②string(8,"a")× 只能是字符类型(无相应的构造函数)
③读入一个带空格的串 getline(cin,s1)
④s2=s1可以直接赋值(右边可以是c的串or字符) +可以直接联结
string s2=s3+' '+"aaa"+s1;
⑤求串长 s1.length() 转化为c风格的字符串 s1.c_str()
8.函数的相关改进:①全局变量的作用从定义点开始到程序结束,但在同名局部变量的作用域内,该全局变 量不可见(实际上是真正的全局,区别c假全局)->方法 ::sum即可表示全局变量中sum (局部变量是不能用的)
②形式参数带有默认值:
(1)若原型中参数值已经给定,定义的首部不能再提供默认参数值,若原型没有,定义的首部即可提供
(2)默认参数值提供的顺序应该从右到左
③函数重载:仅在类型、个数、顺序(简称参数列表)之一中一项or多项不同可以构成重载,返回值类型不构成重载(函数名必须相同)
④引用的定义与应用
9.引用的概念和使用:①在声明一个引用的同时,若不是作为函数的参数or返回值,就必须对它进行初始化保证后续都只是简单的赋值,不能重新改变引用对象)
②只是别名,不另外分配空间(占用同一段空间),地址和值始终相同
③&不是取地址符
④不 能建立void类型引用、引用的引用、指向引用的 指针、引用数组。
⑤const常引用不能够改变原先的名字,b 是a 的别名,a可改值,b不能
⑥引用作为返回值(return +变量 自动局部变量不可以作为引用返回 const常引用不能return
⑦引用可以代替部分指针的应用场景(不是全部)
10.动态内存空间管理:①new是运算符,不是库函数
②
此时可以直接开数组 int p[N];
③new被赋值的类型通常要高一阶 int*p=new int; 开后检查 delete两种删除方法(数组就用数组的!!!) new一个数组的时候,初始化可以用{}而不是()
④C++中两种强转
11.异常处理:①检查try 抛出throw 上级函数捕捉catch
②程序中的错误可分为语法错误(编译和链接错误)和运行错误(通过了语法检测,逻辑错误)
③不能出现其他代码夹在try和catch之间
第三章 类与对象的基本知识
1.类是一种复杂的数据类型(不占有空间),静态属性(数据成员)、动态属性(成员函数) class {};分号别忘记加上
2.所有类成员的名称都是该类的局部变量,在类外使用相同名称不会引起重名。 默认private,内部成员间可以互相访问(成员函数有权访问本类对象的所有成员)
3.类外实现成员函数 用类名限定表示不是一个普通函数 。将成员函数放在类定义中,则自动被视为内敛函数 inline
4.类的任何成员都具有访问属性。
5.在C++程序中,每个成员函数都有一个特殊的隐含指针,称为this指针(static函数没有),这个指针用来存放当前对象的地址。
5.构造、析构函数可以由用户自己写,但调用仍然由系统自动调用
6.构造:①函数名与类名相同②无返回值类型 也不能加void ③必须是public ④不能再程序中再自己调用
⑤构造函数可以重载(避免二义性) ⑥若有带默认参数值的构造函数,默认参数值一定要放在声明中,而不是实现。
7.区分两种写法
①CDate A(2020,3,16);
today=A;(赋值语句)
B本句话并没有拷贝构造!
②today=Date(2020,3,16);
调用构造函数
8.拷贝构造(复制构造函数):
const保证不被修改
三种系统自动调用复制构造函数的情况:
(区分单纯的赋值语句和用一个现存的对象给另一个对象“初始化!)
9.析构函数:①函数名和类名相同,需要在类名前+~②无返回值 void× ③无参数,不能被重载,一个类仅一个,动态多态性时必须写成虚析构函数。
④两种情况下自动调用析构函数:(a)对象生命周期结束时由系统自动调用(b)new动态创建的对象,用delete释放申请的内存时。⑤析构函数的调用顺序与构造函数相反 delete []p 中的[] 不能丢
10.深复制用于数据成员中有动态申请内存的指针类型成员 (自定义深拷贝)
11.对象的应用:①Cdate dt[20];会调用20次无参构造函数 ②Cdate array[3]={Cdate(1,1,1)}并未使用拷贝构造函数,仅构造函数。 无名对象在转交信息后,下一步之前被释放掉。③对象的引用也是共享一个单元,对象的引用也必须在定义时初始化,在初始化后,不会随着赋值语句的执行而成为另一个对象的别名。对象的引用作为参数的时候,不会调用复制构造函数,也无需额外占用空间,将对象引用作为返回值,还可以将函数的调用当做左值使用。(本质:赋值语句)
第四章 类与对象的知识进阶
1.对象成员:①权限:对象成员本身的保护和私有属性仍然是不可直接访问的。②"必考读程题"构造次序问题:遵循“客人优先”的原则,首先调用对象成员的构造函数,再调用自身的构造函数,析构完全相反。
③对象与它内部的对象成员具有相同的生命周期 ④初始化对象成员的方法:必须要用“初始化成员列表”!
2.静态成员:
(1)静态数据成员:
①不管类创建了几个对象,静态成员(包括数据和函数)都只有一份副本,为类所共享
②不能在类定义中进行初始化静态数据成员(类仅是实体对象的抽象,未分配相应的存储空间)
也不能在某个成员函数中进行初始化。
③初始化必须在类外,默认为0(必须要写这一个语句。)类外初始化时就不用static关键字了
例 int Croster:: Count=100; 要加上类名限定。
④放在public属性下,才可以直接访问。两种访问方法: Croster::Count 和list[1].Count
⑤在没有创建任何类对象时,类的静态成员也是存在的。
(2)静态成员函数:
①类外无需static修饰
②静态成员函数无this指针
③静态成员函数只能访问静态数据成员!,不允许访问非静态数据成员。
④同样也是两种调用方法 类名or对象
3.const:
(1)常对象:
①常对象定义时,必须进行初始化,而且不能修改其对象的数据成员值
②常对象不能调用类中的普通成员函数
(2)常成员
(a)常数据成员:
①定义时必须进行初始化!(只能在初始化成员列表中进行,同对象成员。!!)(不能再构造函数的函数体中用赋值语句实现)
②特:静态常数据成员,初始化是在类定以后单独初始化(类外初始化时,static不要,const一定要带上)
(b)常成员函数
①原型声明 : 类型 函数名 (参数表) const (注意const 的位置)
②若有同名函数,是能够构成重载函数的!
③原型声明和函数定义的首部都要加上关键词const
④只能将成员函数声明为const 普通函数不能。常成员函数中的数据成员不可以出现在赋值符号的左侧。
⑤常成员函数不能调用普通成员函数(无const),但普通成员函数是可以调用常成员函数的。
4.友元:
①三种形式:友元函数(无this指针)、友元成员、友元类
②单向性,并只在两个类中有用,无交换性、无传递性
③A的成员函数也可以作为B的友元
④友元函数在类外面给出完整定义,此时前面不再加关键字friend
⑤友元函数也可以在类内部直接给出定义,定义的首部相当于是原型声明,这样的定义默认是内联函数。
⑥友元打破了封装和信息隐藏的机制。
⑦若A被声明为B 类的友元,则A类中的所有成员函数都是B类的友元函数,都可以访问B 类的所有成员
第五章 继承性
1.多重继承的概念:派生类有多个基类 (可重用性)
2.可以缺省继承的方式(默认是private) 3种继承方式的主要区别,访问属性在派生类中会有不同的变化。(例:私有继承的私有成员和私有继承的公有成员的区别->见表)
3.基类的构造函数和析构函数不能被继承
4.基类的私有成员不可被继承,在派生类中不可见(≠不可访问->仍然可有通过接口访问)。
5.属性变化表
6.在派生类中,继承来的成员(基类的私有成员是未被继承的,不可见的)跟新定义的成员一样,访问没有任何限制。
7.其基类成员的初始化则必须在基类的构造函数中完成。在C++中,这个工作借助于派生类构造函数对基类构造函数的调用来实现。
派生类的析构函数只能完成对新增加数据成员的清理工作,而基类成员的扫尾工作则应由基类的析构函数完成。由于析构函数没有参数,因此派生类的析构函数默认直接调用了基类的析构函数。
8.构造和析构的执行顺序问题:
①先构造基类对象成员(尊老+客优先)
②再构造基类自己(尊老
③再派生类的对象成员
④派生类的构造函数
9.派生类只需负责直接基类构造函数的调用。若直接基类构造函数不需要提供参数,则无需在初始化列表中列出,但实质上也是会自动调用基类构造函数的。
10.基类构造函数的调用通过初始化列表来完成。当创建一个对象时,实际调用次序为声明派生类时各基类出现的次序,而不是各基类构造函数在初始化列表中的次序。
11.其他初始化项包括①对象成员、②常成员 、引用成员等。另外,普通数据成员的初始化,也可以放在初始化列表中进行。
12.同名覆盖原则将发挥作用。无论是派生类内部成员函数还是派生类对象访问同名成员,如果未加任何特殊标识,则访问的都是派生类中新定义的同名成员
13.如果派生类内部成员函数或派生类对象需要访问基类的同名成员,则必须在同名成员前面加上“基类名::”进行限定。
14.通过派生类的指针或引用,访问的是派生类的同名成员,此时同名覆盖原则仍然发挥作用。(无virtual看指针or引用类型)
15.虚基类(解决“菱形继承”的问题!):①确保虚基类的构造函数至多被调用一次(虚基类的构造函数将由最远派生类的构造函数调用,其余调用都被忽略)->注:若虚基类没有默认无参构造函数,则一定要在最远派生类的构造函数的初始化列表中单独调用。(最优先构造出虚基类,此后在base1中不再调用base虚基类的构造函数)
②相关调用顺序:先是all虚基类的构造函数(按照定义的顺序
然后直接基类的构造函数 然后 对象成员的构造函数 最后 派生类的构造函数
③若虚基类有直接基类,则先调用虚基类的直接基类。
16.赋值兼容规则:“狼可以当狗来用",此时复制后的引用(基类引用)只能访问基类成员部分,不可以访问派生类新增成员。
"狗不可以当狼用" 基类不能给派生类赋值。 注:必须是public继承
第六章 多态性
1.C++程序的多态的实现分为静态联编(static binding)和动态联编(dynamic binding)
静态联编:是在程序编译阶段就能实现的多态性,这种多态性称为静态多态性,也称编译时的多态性,可以通过函数重载和运算符重载实现
动态联编: 是在程序执行阶段实现的多态性,这种多态性称为动态多态性,也称运行时的多态性,可以通过继承、虚函数、基类的指针或引用等技术实现
2.静态多态性的实现 ——重载.
①静态多态性的优点是函数调用速度快、效率高;缺点是编程不够灵活。
②静态多态性可以通过函数重载和运算符重载实现,运算符重载本质上就是一种特殊的函数重载
③5个不可以重载的运算符
④只能重载已有运算符(保持优先级和结合性,不改变功能和意义和操作数),不能创造新运算符
⑤特殊的:基类中重载的运算符不能被派生类继承的情况
⑥(只用不改,形式参数类型应该是const类型)
⑦成员函数重载 :第一运算对象是当前类的对象(下图为显和隐式的调用方法)
⑧其它补充:
“提取运算符>>和插入运算符<<”的重载
显隐式的不同调用方式
后缀++
注意看!!!C3=operator++(c1,0)是这样调用的,而不是int,传一个类型进去,应该是传一个值进去
3.动态多态性:
①为了实现动态多态性,首先要将该同名函数(函数原型必须完全一样)声明为虚函数(必须是类的非静态成员),在类外定义时,前面就不能再加上virtual。
②虚函数在基类中一定要加“virtual”声明,在公有派生类中,该原型相同的函数前可以省略“virtual”关键字,自动默认该成员函数就是虚函数。
③条件:虚函数(虚函数可以通过基类指针或引用访问基类或派生类中被声明为虚函数的同名函数)(同名虚函数在基类和派生类的函数原型完全一致,否则将无法通过虚函数实现动态多态性)+公有继承public+必须定义基类的指针或引用
④析构函数可以定义为虚函数,在某些情况下还必须定义为虚析构函数(例如:用基类的指针可以申请公有派生类对象的空间,则基类的指针指向了动态派生类的对象。此时需要将析构函数声明为虚析构函数。因为,当用语句“delete 该基类指针;”释放动态派生类对象时,就会调用该指针指向的派生类的析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象被完全释放。)
⑤在基类的成员函数被声明为虚函数后,其公有派生类中要有该虚函数的重新定义版本,也就是说,派生类中的函数原型(包括函数返回值类型、函数名、形式参数表)与基类中的虚函数原型必须完全一致,这样才可以利用基类的指针或引用实现动态多态性(一个函数,如果在基类和其公有派生类中拥有相同的函数名,但是函数返回值类型不同,或者是形式参数表不同,即使在基类中被声明为虚函数,也不具备动态多态性。此时,基类中的函数无虚函数特性,当作普通成员函数使用,而在派生类中存在的同名函数,就是之前所讲到的同名覆盖现象,无法通过基类的指针或引用实现动态多态性。)
4.纯虚函数与抽象类:
①拥有至少一个纯虚函数的类称之为抽象类(抽象类不能生成对象,因为该类中的纯虚函数,无实现代码)(可以定义抽象类的指针或引用,用来实现动态多态性。但是,不能用抽象类作为参数类型、函数返回值类型或显式转换的类型。)(抽象类除了必须至少有一个纯虚函数以外,还可以定义普通成员函数或虚函数。)
②纯虚函数只给出了函数的原型声明而没有具体的实现内容,其声明方式为:在虚函数原型的最后赋值0,声明的一般形式如下:
virtual <返回类型> <成员函数名> (<形式参数表>)=0;
③通常是在一个基类中声明纯虚函数,在该基类的所有派生类中都应该重新定义该函数(保持函数原型完全一致),给出特定的实现代码。
④判断:one 带有虚函数的类称为虚基类 ×
two 带有虚函数的类称为抽象类 ×
three 抽象类只能是基类,不能是派生类 ×
four 抽象类只能当做基类来使用 √
第七章 模板
1.C++有两种模板,类模板和函数模板->经过实例化->得到: 模板函数和模板类(优点:减少冗余信息,提高开发效率,实现更高层次的代码重用。)
(实例化时:参数类型可以是标准类型,也可以是用户自定义类型)
2.函数模板:①template声明关键字,class和typename关键字是一个类型参数
②函数模板代表了一组函数的集合,不是一个实实在在的函数,不可以直接使用,编译系统也不为它产生任何可执行的代码。
③实例化分为显式实例化和隐式实例化
④模板函数的重载
3.类模板:①必须显式实例化!!! Example<int,int> a(10,10);
②定义类模板的方法
③在类外定义成员函数的方式:
第八章 C++文件及输入、输出控制
1.C++语言的I/O流库含有两个平行基类,即streambuf和ios,所有的流类都可以由它们派生出来,流类形成的层次结构就构成了流类库。
2.istream派生出ifstream类,在写重载的时候,可以用ifstream作为左操作数
3.除了用cout对象之外,还可以用cerr对象和clog对象配合插入运算符“<<”控制输出
4.C++语言提供了两种进行格式控制的方法:一种是使用ios类中的有关格式控制的成员函数;另一种是使用操纵符:
操纵符setw(int w)是将域宽设置为w
5.文件的输入和输出:
#include <fstream>
6.若由于某些原因打不开文件,则流变量的值将变位0(返回值) !mystream检测是否打开成功。
最后记得mystream.close();
7.文件的读写(一共三种方式):
①提取和插入运算符
②get()和put():
get( )与put( )函数一般成对使用,可以用于对文本文件或二进制文件的读写,每次读写1字节(字符)。
get( )是输入流类istream中定义的成员函数,可以从与流对象连接的文件中读出数据,每次读出1字节(字符)
put( )是输出流类ostream中定义的成员函数,它可以向与流连接的文件中写入数据,一次写入一个字节(字符)
③read()和write():
read()与write()函数一般成对使用,既可以用于对文本文件的读写,也可以用于对二进制文件的读写,每次读写一个数据块,一般主要用于对二进制文件的读写
read()是输入流类istream中定义的成员函数,其最常用的函数原型如下: istream & read(char *buf , int num ) ;
第1个参数buf是一个字符型指针,是读入的数据存于内存的首地址;第2个整型参数num规定了一次读入的字节(字符)数
write()是输出流类ostream中定义的成员函数,其最常用的函数原型如下:
ostream & write(const char *buf,int num) ;
将string类的字符串,用str.c_str( )函数转换为C风格的字符串,复制到s数组管理的C风格字符串,这样,第27行用write写文件时,第一实参用s就符合了函数的要求了。
第二个参数Num(表示一次字节数)常用sizeof()