c++面向对象的三大特性
- 封装;将相同属性的数据和方法封装在一起,加权限区分,用户只能借助公共方法操作私有数据.
- 继承:体现在类和类之间的关系,如果A类继承B类,那么A类直接拥有B类的数据和方法.
- 多态:一个接口(函数),多种功能.
作用域运算符::
::解决归属问题(谁是谁的谁)
结构体类型增强
- c++允许函数作为结构体成员
新增BOOl类型
占1个字节;返回值true或者false;
三目运算符
c++返回的是引用,而c语言返回的是值;
int a=10;
int b=20;
a>b?a:b=10;(c++允许,c语言报错)
左值和右值
左值:能被赋值的值.(在等号的左边)
右值;只能放右边的值.(不能被赋值的值)
const增强
- 尽量使用const代替#define
- const有类型,可以进行编译器类型的安全检查.#define无类型,不可以进行类型检查
- const有作用域,而#define不重视作用域,宏不能作为命名空间,结构体,类的成员,而const可以.
引用
- 引用的本质:就是给变量名取个别名
- 引用定义的步骤:
1.&别名
2 给哪个变量取别名就定义该变量
3 从上往下整体替换
内联函数
- 在编译阶段,将内联函数中的函数体 替换函数调用处.避免函数调用时的开销.
- 内联函数:必须在定义的时候使用关键字inline修饰,不能在声明的时候使用inline.
- 宏函数和内联函数的区别:
同:宏函数和内联函数 都会在适当的位置进行展开,避免函数调用开销
异: 宏函数的参数没有类型,不能保证参数的完整性.
内联函数的参数有类型,能保证参数的完整性.
宏函数在预处理阶段展开
内联函数在编译阶段展开
宏函数没有作用域的限制,不能作为命名空间,结构体,类的成员
内联函数有作用域的限制,能作为命名空间,结构体,类的成员. - 内联函数的注意事项
在内联函数定义的时候加inline修饰
类中的成员函数默认都是内联函数(不加inline 也是内联函数)有时候就算加上inline也不一定是内联函数(内联函数条件)
条件:->1.不能存在任何形式的循环语句
2.不能存在过多的条件判断语句函数体不能过于庞大
3.不能对函数取地址
有时候不加inline也可能是内联函数
函数重载
- 函数重载是c++的多态的特性(静态多态)
- 函数重载:用同一个函数名,代表不同的函数功能.
- 函数重载的条件:
1.同一作用域,函数的参数类型,个数,顺序不同都可以重载
c++中不能直接将函数名作为函数的入口地址—>函数名和参数共同决定函数的入口地址.
缺省参数
在函数声明处,给函数参数一个默认的值,如果函数调用处,用户没用传实参,编译器就可以使用这个默认的值.
注意:如果函数的某个参数设置为默认参数,那么这个参数的右边的所有参数都必须是默认参数.
类
- 类将数据和方法封装在一起,加以权限区分.用户只能通过公共方法访问私有数据.
定义一个类 关键字class
- 类的权限分为:private,protected,public.但是在类的内部不存在权限之分.只是对类外有效.
- 如果类不涉及到继承,private,protected没有区别,都是私有属性.
构造函数
- 构造函数:完成对象的初始化,系统自动变量.
- 析构函数:完成对象的释放,系统自动调用.
构造函数的定义:
构造函数函数名和类名相同,没有返回值类型,连void都不可以,但可以有参数(重载).
先给对象开辟空间(实例化),再调用构造函数(初始化).
构造函数的调用时机:
如果用户不提供任何构造函数,编译器默认提供一个空的无参构造.如果用户定义了>构造函数(不管是有参还是无参),编译器不再提供默认构造函数
析构函数
- 当对象生命周期结束的时候,系统自动调用析构函数.
- 函数名和类名称相同,在函数名前加~,没有返回值类型,没有函数形参.(不能被重载).
先调用析构函数,再释放对象空间. - 一般情况下,空的析构函数就足够.但是如果一个类有指针成员,这个类必须写析构函数,释放指针成员所指向空间.
拷贝构造函数
- 拷贝构造:本质是构造函数
- 拷贝构造调用时机:旧对象.初始化.新对象,才会调用拷贝构造.
- 如果用户不提供拷贝构造, 编译器会自动提供一个默认的拷贝构造(完成赋值动作—浅拷贝).
拷贝构造和无参构造 有参构造的关系
如果用户定义了拷贝构造或者有参构造 都会屏蔽无参构造.
如果用户定义了无参构造或者有参构造,不会屏蔽拷贝构造
- 拷贝构造几种调用形式
1.旧对象给新对象初始化 调用拷贝构造.
2.给对象取别名 不会调用拷贝构造.
3.普通对象作为函数参数,调用函数时会发生拷贝构造.
4函数返回值普通对象(Visual Studio会发生拷贝构造)(Qtcreater,linux不会发生).
- 拷贝构造的深拷贝和浅拷贝
默认的拷贝构造 都是浅拷贝
如果类中没有指针成员,不用实现拷贝构造和析构函数
如果类中有指针成员,必须实现析构函数释放指针成员指向的堆区空间,必须实现拷贝构造完成深拷贝动作.
对象数组
对象数组:本质是数组,数组的每个元素是对象.
explicit关键字
- explicit防止构造函数隐式转换
//构造函数隐式转换(类中只有一个数据成员)
new和delete堆区空间操作
1.new和delete操作基本类型的空间
- new和malloc delete和free没有区别
- 区别:new不用强制类型转换;new在申请空间的时候可以初始化空间内容.
2.new和delete操作类空间- malloc不会调用构造函数 free不会调用析构函数
- new 会调用构造函数 delete调用析构函数
- 基本类型的对象没有析构函数,因此回收基本类型的数组空间用delete和delete[]都可以,但为了规范,不建议用delete。
静态成员
定义:类的对象 拥有独立的普通成员数据
static修饰的成员叫静态成员.
static修饰的静态成员数据必须类中定义 类外初始化.
静态成员数据,可以通过类名称直接访问(属于类)
单例模式
- 单例模式的类只能实例化一个对象.
this指针
- 普通成员函数 默认有一个this指针 指向调用该成员函数的对象.
- this完成链式操作.
友元
- 类将数据和方法封装在一起 加以权限区分 用户只能通过公共方法操作私有数据(封装性)
- 主要用于运算符重载.
- 一个函数或者类 作为了另一个类的友元 那么这个函数或类就可以直接访问另一个类的私有数据.
运算符重载
- 运算符重载是对已有的运算符指定新功能.不能创建新运算.
运算符重载关键字operator
思路:
1.弄懂运算符的运算对象的个数(个数决定了重载函数的参数个数)
2.识别运算符左边的运算对象是类的对象还是其他.
类的对象:全局函数实现(不推荐) 成员函数实现(推荐,少一个参数)
其他:只能是全局函数实现.
3.可以重载的运算符
重载++运算符
++a(前置++),它就调用operator(a).
a++(后置++),它就调用operator++(a,int).
函数模板
- 将功能相同,类型不同的函数(类)的类型抽象成虚拟的类型.当调用函数(类实例化对象)的时候,编译器自动将虚拟的类型具体化.这个就是函数模板(类模板).
- 关键字template;
函数模板会编译两次:
第一次:是对函数模板本身的编译
第二次:函数调用处将T的类型具体化
函数模板目标:模板为了实现泛型,可以减轻编程的工作量,增强函数的重用性.
- 1.函数模板和普通函数都识别.(优先选择普通函数)
- 2.如果想强制使用函数模板
-
e.g swapall<>(a,b);(加上<>)
- 3.函数模板自动类型推导时,不能对函数的参数进行自动类型转换.
- 4.如果函数模板显示指明T的具体类型,这时函数模板的参数可以自动类型转换.
5.模板的局限性 - 当函数模板推导出T为数组或者其他自定义类型数据,可能导致运算符不识别.
解决方法一:运算符重载
方法二:将函数模板具体化.
纯虚函数
- 只要有一个纯虚函数,这个类称为抽象类.
- 抽象类特点:
-
1.无法实例化对象
-
2.抽象类的子类,必须要重写父类中的纯虚函数,否则也属于抽象类.
虚析构与纯虚析构
多态使用时,如果子类中有属性开辟到堆区,父类指针在析构时候,不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏.
- 虚析构和纯虚析构共性:
可以解决父类指针释放子类对象时不干净的问题.
都需要具体的函数实现. - 区别:
- 如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法: virtual ~类名(){ }
纯虚析构语法: virtual ~类名()=0; 类名::~类名(){};
- 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构.
类模板
- 类模板将类中类型抽象成虚拟类型.
- 1.类模板 实例化对象不能自动类型推导
- 2.类模板 实例化对象必须指明T的类型.
STL的概述
STl的六大组件:容器,算法,迭代器,适配器,仿函数,空间配置
- 容器:存放数据
- 算法:操作数据
- 迭代器:算法通过迭代器操作容器
- 适配器:为算法提供更多的接口.
- 仿函数:为算法提供策略
- 空间配置:为算法,容器提供动态空间.
Vector容器
- vector容器:单端动态数组容器.
push_back尾部插入元素,pop_back尾部删除元素
front()头元素,back()尾元素
begin()得到的是容器的起始迭代器(首元素的位置)
end()得到的是结束迭代器(尾元素的下一个元素位置)
必须包含头文件:#include
deque容器
- 双端动态数组
- Deque容器和vector容器最大差异
1.deque允许使用常数项时间对头端进行元素的插入和删除操作
2.deque没有容量的概念.
stack栈容器
- stack是一种先进后出的数据结构
- 操作数据的一端叫栈顶.
- top永远指向栈顶元素.
- 栈容器没有迭代器.不支持遍历行为.
queue队列容器
- queue是一种先进先出的数据结构.
- 出数据的一端叫队头,入数据的一端叫队尾.
- queue容器没有迭代器,不支持遍历行为.
list容器
- 双向循环链表
- list的迭代器是双向迭代器.
set容器
- set容器只有键值,在插入数据的时候自动根据键值排序.不允许有相同的键值.不能修改set容器的元素值,会破坏set的数据结构.set容器的迭代器是只读迭代器(const_iterator).