C和C++,是很多程序员必修之课
C作为编写操作系统的语言 速度是高级语言里最快的
C能直接操作内存,学习的途中可以加深对计算机运行时候,内存管理的理解
C++可以更快速的编写C的工作的代码,是写了很多软件的语言
基础
命名空间
命名空间可以防止,两个类的成员和方法命名冲突 可以使用using关键字,来引入命名空间 例如:using namespace std;
别名
可以在函数的形参中在变量前加 & ,使得实参的传入变成实参取别名的方式传入,可以直接对原来的数据就行修改,对比同样方式的指针操作 更直观
定义常量的方式const
可以在函数的形参中在变量前加const 关键字,使得传入的形参变成常量,无法进行修改.也可以在指针前加关键字,使得指针无法修改为别的地址
函数重载
函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。
可以定义多个同名函数,函数参数,类型和默认值的不同进行重载
普通函数和内联函数
普通函数运行时的数据在栈里,而内联函数直接编译在代码段里 在函数前面加上关键字inline定义 但是只能定义简单逻辑的函数
内存的申请和删除
使用关键字 new delete 运算符 操作 操作的内存数据在堆中,申请的内存需要手动释放,不然会造成内存泄漏 列如:int *p = new int; delete p;
对象 三大特性 封装–继承–多态
–封装–特性
构造函数
与类同名的函数 在创建对象时候自动调用
析构函数
在类名前面加上~符号的函数 在销毁对象的时候自动调用
拷贝构造函数
与构造函数的函数名相同,形参不同 列如 CExample(const CExample& C) 拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量
在C++中,下面三种对象需要调用拷贝构造函数!
- 对象以值传递的方式传入函数参数
- 对象以值传递的方式从函数返回
- 对象需要通过另外一个对象进行初始化;
浅拷贝就是拷贝构造函数没有处理静态数据成员,只对对象中的数据成员进行简单的赋值
深拷贝是把静态数据成员,创建新的数据对象在新的对象中传入
通过对对象复制的分析,我们发现对象的复制大多在进行“值传递”时发生,这里有一个小技巧可以防止按值传递——声明一个私有拷贝构造函数。甚至不必去定义这个拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类对象,将得到一个编译错误,从而可以避免按值传递或返回对象。
初始化列表
Test2(Test1 &t1):test1(t1){}
构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段。
以下几种情况时必须使用初始化列表
- 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
- 引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
- 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。
–继承–特性
三种类型: 公有继承 public 保护继承 protected 私有继承 private
继承的数据相同,成员访问限定符可以降级 public继承不变 protected使得public变成protected private继承使得所有成员变为private 父类的private成员不会被继承
隐藏
在子类中定义与父类的同名方法 可以使得父类的方法隐藏 然后可以用parent::function命 的形式调用
多继承与多重继承
子类同时可以继承多个父类(多继承),也可以进行多级的继承(多重继承)
虚继承
为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题。
class 派生类名:virtual 继承方式 基类名
virtual是关键字,声明该基类为派生类的虚基类。
在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
声明了虚基类之后,虚基类在进一步派生过程中始终和派生类一起,维护同一个基类子对象的拷贝。
–多态–特性
虚函数
子类继承使用虚函数进行函数的覆盖
态类型:具有继承关系的多个类型
知识点1:虚函数(virtual 函数名();)——解决“如果你以一个基类指针指向一个派生类对象,那么通过该指针,你只能访问基础类定义的成员函数”此问题。
原因:如果你以一个基类指针指向一个派生类对象,那么通过该指针,你只能访问基础类定义的成员函数;如果以派生类指针指向基类对象,这种做法相当危险,一般不会这么定义
知识点2:同名成员函数——如果基类和派生类定义了相同的成员函数,那么对象指针调用成员函数时,到底调用的是哪个成员函数,最终取决于这个指针的原型,而不是指向的对象类型,不过虚函数可以解决此问题,可以在“一个基类指针指向一个派生类对象”的情况下,调用派生类对象的同名成员函数
知识点3:虚析构函数(virtual ~类名();)——解决在动态多态中,内存泄漏的问题
知识点4:virtual限制用法——不能修饰类外申明并定义的函数(全局函数),即只能修饰类内申明并定义的函数(通常在申明时修饰即可);virtual不能修饰类内的静态成员函数;virtual不能修饰内联函数;virtual不能修饰构造函数。
知识点5:继承虚函数——注意上述,虚函数和虚析构函数,一定要在基类里给函数或者析构函数添加virtual关键字。若基类里,申明虚函数,则派生类里会默认继承虚函数,当然最好也给其他类加上virtual关键字
注:在C++11新标准中,可以使用override关键字来说明派生类中的虚函数。
知识点6:析构函数最好都添加virtual关键字,使之成为虚析构函数,这样避免之后出现的内存泄漏问题(即内存占用不释放),因为并不知道未来继承的子类会不会在其构造函数中申请内存
知识点7:虚函数原理
指针指向对象——对象指针;指针指向函数——函数指针
申明基类的虚函数时,会自动生成虚函数表,用于寻找虚函数的入口地址,如果派生类继承基类的虚函数,且没有自定义同名虚函数时,则也会自动生成虚函数表,而自动生成的虚函数表指针,会指向基类虚函数的相同入口地址;但如果派生类继承基类,并自定义同名虚函数时,则会自动生成虚函数,但指向与上述不同的虚函数入口地址(函数的覆盖)。
知识点8:函数的覆盖和隐藏
当基类和子类出现同名函数时,此时出现函数的隐藏;但若定义了虚同名函数,则此时出现函数的覆盖。(可参考知识点7,即虚函数表的地址异同问题)
知识点9:虚析构函数原理
理论前提:执行完派生类的析构函数就会执行基类的析构函数
当在基类中,申明虚析构函数后,则在派生类中,也会在默认生成虚析构函数,即在派生类的析构函数前,默认加上virtual关键字
这样可以解决内存泄漏问题,即解决了“释放了指向基类对象的指针所占内存,但忘了释放派生类里分配给另一指针的内存(假如另一指针存在的话)”,因为派生类的虚函数指针表的作用(请参考知识点7),结果就是先执行派生类的析构函数,再执行基类的析构函数,从而无内存占用,解决内存泄漏问题。
知识点10:对象大小(sizeof(对象名))
这表明对象中的数据成员占用的内存大小,并不包含成员函数;若对象中,无数据成员,则默认分配其一个字节内存大小(若包含虚函数或虚析构函数,则生成虚函数表,此时占用4个字节的内存,即虚函数表指针的大小)。
注:一个指针所占用4个字节的内存
知识点11:虚函数表
a.当类中定义了虚函数或者虚析构函数时,则在类实例化对象时,自动生成虚函数表,并在对象中生成一个虚函数表指针
b.在多态的情况下,虚函数表指针在对象中所占据的内存位置是对象前4个寄存内存单元,后面排列的是其它数据成员
c.每个类只有一份虚函数表,所有该类的对象共用同一张虚函数表
运行时类型识别
通过RTTI,能够通过基类的指针或引用来检索其所指对象的实际类型。c++通过下面两个操作符提供RTTI。
(1)typeid:返回指针或引用所指对象的实际类型。
(2)dynamic_cast:将基类类型的指针或引用安全的转换为派生类型的指针或引用。
对于带虚函数的类,在运行时执行RTTI操作符,返回动态类型信息;对于其他类型,在编译时执行RTTI,返回静态类型信息。
纯虚函数
virtual 函数类型 函数名()=0;//纯虚函数
只有函数申明,没有函数定义的函数也是纯虚函数。
抽象类
定义:含有纯虚函数的类叫做抽象类
抽象类无法实例化对象;抽象类的子类也可以是抽象类,但子类若将抽象类的所有函数都做了实现,即内部有程序,则子类可以实例化对象。
如果一个类含有纯虚函数(抽象类),则无法实例化;但子类可以实例化对象
接口类
定义:仅含有纯虚函数的类(即无任何数据成员,仅含纯虚函数)
用法:定义一个基类,作为接口类,用接口类指针指向其派生类的对象,借此可以调用派生类的同名虚函数(在接口类中是纯虚函数,在派生类中是虚函数),提现接口的作用。
扩展:接口类也是抽象类;不能用接口类实例化对象(即接口类可以被继承,但不可实例化对象);一个类既可以继承一个或多个接口类,也可以同时继承非接口类;可以使用接口类指针指向其子类对象,并调用子类对象中实现的接口类纯虚函数。
static静态成员
保存在类中的数据只有一份,在对象中通用,与全局变量类似
友元函数
可以在类中定义全局友元函数 和类友元函数 定义之后 可以在函数中调用类的所有成员
运算符重载
可以在类中定义运算符重载函数 来支持运算符可以进行类的运算 包括一元运算符和二元运算符
模板函数和模板类
对于运算过程不变类型变换的函数和类 可以分别使用模板函数和模板类
C++标准模板类STL
是C++中编译器实现的内置容器类
vector向量 list链表 map映射等
可以配合迭代器使用