c++下
树图思维导图提供 c++下 在线思维导图免费制作,点击“编辑”按钮,可对 c++下 进行在线思维导图编辑,本思维导图属于思维导图模板主题,文件编号是:1dc168662ab86138a22bb4ba8e9db865
c++下思维导图模板大纲
类
概念:
把对象的属性和操作结合成一个独立的系统单 位,尽可能隐蔽对象的内部细节,对象的属性只能由这个对象的操作来存取
权限
public:类外可以访问
protected:类外不可访问
private:类外不可访问
成员函数类外实现:<type><classname>::<funcname>(<参数列表>){}
构造函数
定义:<classname>(<参数列表>){}
特点
定义对象时自动调用,不能显示调用
类名就是函数名,无返回值
一个类可以有多个构造函数重载,无定义是系统默认产生默认构造函数
用户定义了构造函数,系统就不再产生默认构造函数
new动态建立对象数组时,最好具有无参数的默认构造函数
参数化列表:类名(类型1 形参1,类型2 形参2, …): 数据成员1( 参数表1), 数据成员2(参数表2), …{ }
析构函数
定义:~<classname>(<参数列表>){}
特点
无返回值
不支持重载,每个类有且只有一个析构函数
可以被显示调用,但系统调用依然会被执行
析构对象的时候自动调用
执行顺序:构造函数的调用顺序是定义对象的顺序,析构函数调用顺序与之相反
拷贝构造函数
定义:<classname>::<classname>([const]<classname>&<引用名>)
调用时机
用类的一个对象去初始化另一个对象
函数的形参是类的对象的时候(值传递)
函数的返回值是类的对象
参数必须为对象的引用,不是引用的话会触发死循环
深浅拷贝问题
原因:数据开辟到堆空间时,调用系统默认拷贝构造函 数时,会直接把指针地址赋给新的指针,导致两 个指针指向同一块内存,析构时会造成堆区内存 的重复释放
解决:在拷贝构造函数中为新指针重新开辟一块空间,空间内存放相同的数据
静态成员
静态成员变量
定义:static<type>variable
静态成员变量被该类的所有对象共享,无论建立多少个该类的对象,都只有一个静态成员变量存储空间
类外只能访问public权限的静态成员变量
静态成员变量在编译阶段分配内存,在全局区
静态成员只能在类内声明,在类外初始化
访问方式
通过对象访问:variable.staticmember
通过类作用域访问:classname::staticmember
静态成员函数
定义:static<type><functionname>(<args>){}
注意
静态成员函数只属于类
静态成员函数只能直接引用静态成员变量,如需引用非静态成员变量,可以通过对象来引用
访问方式
通过对象访问:Variable.StaticFunc
通过类作用域访问 ClassName::StaticFunc
静态对象
如果静态对象定义在函数中,则函数退出后,静态变量不释放
静态变量只需要定义一次
this指针
定义
c++为非静态成员函数提供的指针
当创建一个类的对象时,系统会自动生成一个 采用动态存储分配,不会造成内存浪费和溢出 this指针,并将this指针的值初始化该对象的地 址
当非静态成员函数通过某个对象被调用时,会自 动传递this指针,指向这个对象
作用
当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中返回对象本身,可使用 return *this;
const常
常对象
定义:const <ClassName> <obj>; 或 <ClassName> const <obj>;
注意:常对象在声明时必须进行初始化,只能调用类的常函数以及类的静态成员函 数
常数据成员
定义 const <type> variable;
注意:常数据成员必须初始化,而且只能在构造函数中通过初始化列表方 式完成初始化
常函数成员
定义 <type> <FunctionName> (<args>) const;
注意:常函数成员只能引用本类中的数据成员,且不能修改,在声明函数和定义函数时都要有const关键字
常引用
定义 const <type> & variable;
作用
保护数据不被更改
赋值操作时若传入非对象,可以产生匿名临时对 象来承接
参数传递时系统不会再分配空间,提高运行效率
左值和右值都可以赋给常引用,而普通引用只有左值可以赋予
常指针
指针常量
const<type>*ptr
指针的指向可以修改,指针指向的值不可以修改
常量指针
<type> * const ptr;
指针的指向不可修改,指针指向的值可以修改
const既修饰指针,又修饰常量
const <type> * const ptr;二者都不可被修改
友元
分类
全局函数做友元
定义 friend <type> <FunctionName> (<args>);
一个函数可以是多个类的友元函数,只需在各个 类中分别声明
在类外定义时不能加friend关键字
友元普通函数形参表中应含有相关类的对象
友元普通函数一般用于读取对象中的成员数据, 而不是修改
类做友元
定义 friend class <ClassName>;
成员函数做友元
定义:friend <type> <ClassName>::< FunctionName> (<args>);
友元关系不能被继承 友元关系是单向的,不具有交换性 友元关系不具有传递性
意义
提高代码的复用性,减少重复代码
语法
class derived_class_name: access_ control_1 base_class_1,access_control_2 语法 base_class_2,…
继承方式(一般情况下:将基类数据定义为protected,并选择public继承方式,是一种比较好的继承方式。)
1
公有继承 public
基类的公有成员和保护成员在派生类中仍然保持为公有成员和保护成员的访问权限
基类的私有成员在派生类中不可访问,只能通过基类的成员函数或友元函数可以访问
2
保护继承 protected
基类的公有成员和保护成员在派生类中为保护成 员的访问权限,派生类的成员函数或友元函数能 访问它们,而在类外不可直接访问
基类的私有成员在派生类中不可访问,只能通过 基类的成员函数或友元函数可以访问
3
私有继承 private
基类的公有成员和保护成员在派生类中的访问权 限是派生类中的私有成员,派生类的成员函数或 友元函数能访问它们,而在类外不可直接访问。
冲突、支配和赋值兼容
冲突
多重继承时,如果多个基类中具有相同名字的成 员,且在基类中的访问权限为公(public)或 保护(protected),当派生类使用到该基类中 的成员时,将会出现不唯一性,称为冲突
解决方法:通过类作用域加以区分
支配
在继承与派生时,如果基类中访问权限为public 或protected成员和派生类添加的新成员同名 时,不会引起冲突,派生类的成员将覆盖基类中 的同名成员。这种优先关系称为支配规则
解决方法:如果需要使用基类中的成员, 需使用 作用域操作符,强调说明属于基类的成员
注意:基类中有多个同名函数的重载,一并都会 被覆盖,同名即覆盖
赋值兼容
由于派生类中包含从基类继承的成员,因此可以 将派生类对象的值赋给基类对象,称为赋值兼容 规则
单向赋值 (派生类对象值可赋值给基类对象值,反 之不行)
可以将派生类对象的地址赋值给基类对象的指 针(即指向基类对象的指针也可以指向派生类对 象)
派生类对象可以初始化基类的引用
构造、析构、拷贝构造
构造函数
基类的构造函数不能被派生类所继承
在设计派生类的构造函数时,不仅要考虑派生类 增加的数据成员的初始化,而且还应考虑基类的 数据成员初始化。
在定义派生类构造函数时,在参数初始化表中写 上基类构造函数名和相关参数
派生类名(<形参表>): 基类名1(<实参表>), …
三种情况可以不写基类构造函数名
基类中没有定义构造函数
定义了没有参数的构造函数
定义的构造函数的所有参数都有默认值
先执行基类构造函数,后执行派生类构造函数
析构函数
派生类不能继承基类的析构函数,因此如果需 要,应在派生类中重新定义析构函数。 执行派生类的析构函数时,基类的析构函数也被自动调用
执行顺序:先执行派生类自己的析构函数,然后 执行基类的析构函数
拷贝构造函数
要求为基类相应的拷贝构造函数传递参数
派生类名(派生类名 &obj): 基类名1(obj), …
菱形继承
定义
如果一个派生类有两个或更多个基类, 那么这种行为称为多继承
两个类继承同一个基类,又有某个类同时继承两 个派生类,这种继承被称为菱形继承
如果类A有成员函数Show(),通过D的对象去访 问A类的成员函数Show(),这时由于类B和类C 都有继承来自于类A的Show()函数, 编译器无法 确定使用哪一个, 从而将发生编译时错误
子类继承两份相同的数据,导致资源浪费以及毫 无意义
利用虚继承可以解决菱形继承的问题
虚继承
虚基类
语法 class 派生类名: virtual 继承方式 基类名
原则
在内存中只有基类成员的一份拷贝,通过把基类 声明成虚拟的,就只能继承基类的一份拷贝,消 除歧义
虚基类并不是在声明基类时声明的,而是在声明 派生类指定继承方式时声明的
构造函数
如果在虚基类中定义有带参数的构造函数,并且 参数没有默认值,而且没有定义无参构造函数, 则在虚基类的直接派生类或间接派生类的构造函 数的初始化表都要对虚基类进行初始化
在执行直接派生类的构造函数时不调用虚基类的构造函数,而是在最终派生类的构造函数中直接调用虚基类的构造函数
编译时多态
运算符重载
概念和语法
概念
一种特殊的运算符重载,允许重新定义已有的运 算符,赋予其另一种功能,以适应不同的数据类 型
语法 <type> ClassName::operator Op_Name(< args>){}
分类
类内重载运算符
左操作数为类的对象时,触发重载机制
全局函数重载运算符
左操作数可以是任意类型,如果传入非对象,生 成临时对象作为参数传入
函数中只要有一个为类对象,则触发重载
需要在类中声明为友元函数
只允许成员函数实现的运算符:=、->、[]、() 只允许友元函数实现的运算符:<<、>>
四则运算符
成员函数和友元函数均可实现
语法
如:friend Complex operator + (const Complex & c1,const Complex & c2)
输入输出运算符 << >>
只能重载为类的友元函数(左值必须是快递小哥)
语法
friend istream & operator>>(istream & os,类名 & obj);
friend ostream & operator<<(ostream & os, const 类名 & obj);
赋值运算符 =
只能重载为类的成员函数(避免二义性)
语法:类名 & operator=(const 类名 &源对象)
如果用户没有重载赋值运算符,编译程序将生成 一个默认赋值运算符函数
涉及到资源问题,必须要重载赋值运算符,进行 深拷贝操作
关系运算符
语法
如::friend bool operator==(const Person& p1,const Person&p2)
运行时多态
定义与问题
发出同样的消息在被不同类型的对象接收时,将 导致完全不同的行为
由于赋值兼容规则,父类的指针或引用指向子类 对象时,只会调用父类的函数,而覆盖了子类的 同名函数
解决:虚函数
使用原因:本质是继承中向后兼容的需求, 使用父类指针指 向派生类对象,这与继承中的向前兼容的赋值运 算矛盾
意义
应用程序不必为每一个派生类编写功能调用, 只 需要对抽象基类进行处理即可。 大大提高程序的 可复用性
派生类的功能可以被基类的方法或引用变量所调 用, 这叫向后兼容,可以提高可扩充性和可维护 性
虚函数
定义
虚函数是为了实现运行时多态而设计的一种特殊 函数,是允许被其派生类重新定义的成员函数
语法
virtual <返回类型> <成员函数名>(参数表) {< 函数体>}
当基类中把一个成员函数说明为虚函数, 则由该 基类所派生的所有派生类中, 该函数一直保持虚 函数的特性(一虚到底)
重写函数不管有无关键字virtual说明, 该成员函数都将成为一个虚函数
虚函数不能声明为静态成员函数,构造函数也不能声明为虚函数
引用只能指代固定的对象,而指针可以随时改变 指向,因此实现多态更多地采用指针方式
实现原理:虚函数有一个虚函数指针和虚函数列表
使用条件: 父类的指针或引用指向子类对象
满足条件
有继承关系
子类重写父类的虚函数
纯虚函数和抽象类
纯虚函数
有时在基类中将某一成员函数声明为虚函数, 是 考虑到派生类的需要, 只在基类中预留了一个函 数名,让具体功能在派生类中根据需要去实现
语法 virtual 返回值类型 函数名(形参表) = 0;
抽象类
定义 包含纯虚函数的类称为抽象类
注意
抽象类无法实例化对象
子类必须重写抽象类中的纯虚函数,否则也属于 抽象类
虚析构和纯虚析构
背景:多态使用时,如果子类中有属性开辟到堆区,那 么父类指针在释放时无法调用到子类的析构代码
解决方式: 将父类中的虚构函数改为虚析构或纯虚析构
语法
virtual ~类名( ){}
virtual ~类名( ){}=0
两者共性
都可以解决父类指针释放子类对象的问题,都需要有具体的函数实现,不可以是空实现
注意
如果是纯虚析构,该类属于抽象类,无法实例化 对象
本质:将所处理的数据类型说明为参数
意义 减少重复代码的编写,大大提高代码的复用性
函数模板
建立一个通用函数,其函数返回值类型和形参类型可以用一个虚拟类型来代表
语法 template <typename T>
使用方式
自动类型推导
显示类型指定
由通用函数模板(一般类型)生成的函数,称为模板函数(实例)
区别
普通函数调用时可以发生隐式类型转换
函数模板调用时,如果利用自动类型推导,不会 发生隐式类型转换
如果显示指定类型,可以发生隐式类型转换
函数模板可以发生重载,包括模板间的重载以及与普通函数的重载
编译器优先匹配类型完全相同的函数,如果匹配 造函数 失败,再寻求函数模板进行匹配
可以通过空模板参数列表来强制调用函数模板 FunctionName<> (<args>);
注意事项
自动类型推导必须推导出一直的数据类型,才 可以使用
模板必须要确定出T的数据类型,才可以使用
类模板
建立一个通用类,类中的成员数据类型可以用一 个虚拟的类型来代表
语法
template <class T>
template<class T, int Number>,可包含非类型参数,调用模板的实参必须是一个 整数或整数常量表达式
template<class T, class T =Specific_Type> 可包含指定数据类型
注意事项
类模板的使用只能用显示类型指定的方式
类模板中的成员函数再调用时才创建
类模板成员函数类外定义
template < typename T1, typename T2, …>ClassName <T1, T2, …> :: ClassName(< args>) 构造函数
template < typename T1, typename T2, …><type> ClassName <T1, T2, …> ::FunctionName(<args>) 成员函数
友元
直接在类内声明友元 类内实现
需要提前让编译器知道全局函数的存在,即:类模板声明→类声明→函数模板声明→函数模板定 义→友元
继承
子类继承的父类是一个类模板时,子类在声明的 时候,要指出父类中T的类型
如果想灵活指定父类中T的类型,子类也许变为类模板
顺序表
定义:直接随机访问的线性表
特点
一次建立,连续,有界
元素在内存中顺序排列
通过下标访问
访问元素的时间开销与表的长度无关
插入/删除表元素时间开销与表的长度有关
属性
大开小用(防止溢出,数组大小不可改变)
表头位置(通常是0)
表尾位置(不超过数组大小的任意位置)
表长=表尾位置 - 表头位置 + 1
最大表长=数组大小
主要操作
从前向后,逐个比对 查找元素
从后向前,逐个后移 插入元素
从前向后,逐个前移 删除元素
顺序链表
定义
物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现
链表由一系列结点(链表中每一个元素称为结 点)组成,结点可以在运行时动态生成
每个结点包括两个部分:存储数据元素的数据 域、存储下一个结点地址的指针域
分类
单向链表
特征
分次建立,离散,无界
间接顺序访问:只能由表头开始逐个访问各个节点
只能访问后继结点,不能访问前驱节点
访问表成员时间开销与位置有关
插入/删除表成员时间开销与位置无关
构成
结点(多个)(必要元素)
表头指针head(必要元素)
表尾指针tail(非必要元素)
双向链表
单向循环链表
双向循环链表
常用操作
插入节点
中间插入结点
修改新结点后继,指向原结点后继
修改原结点后继,指向新节点
表头插入结点
修改新结点后继,指向head
修改head,指向新结点
表尾插入结点
修改新结点后继,指向NULL
修改tail,指向新结点
查找节点
表头head不动,用指针p从表头起访问每个结点
找到了:p指向结点是要找的结点
找完了:p指向表尾的后继,也就是NULL
删除结点
标记被删节点为delnode
被删结点后继指向delnode的后继
释放delnode
栈
定义
一种运算受限的线性表,仅允许在表的一端(栈 顶)进行插入和删除运算
分类
顺序栈
链栈
基本原则
后进先出
常用操作
进栈
出栈
清空
队列
定义
一种运算受限的线性表,其限制是只能在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作 定义
循环队列
(rear+1)%maxsize==front
链队列
队首出队,删除表头
队尾进队,添加表尾
基本原则
先进先出
常用操作
入队
出队
清空
文件流
文本文件
读
1、包含头文件 #include <fstream>
2、创建流对象 ifstream ifs;
3、打开文件并判断文件是否打开成功 ifs.open(" 文件路径",打开方式);
4、读数据(四种方式)
5、关闭文件 ifs.close();
写
1、包含头文件 #include <fstream>
2、创建流对象 ofstream ofs;
3、打开文件 ofs.open("文件路径",打开方式);
4、写数据 ofs<<"写入的数据";
5、关闭文件 ofs.close();
注:fstream类型可以同时实现读写操作
二进制文件
打开方式指定为 ios::binary
读
read(char * buffer, int len) 读
write(const char* buffer, int len) 写
文件打开方式
为读文件而打开文件 ios::in
为写文件而打开文件 ios::out
初始位置:文件尾 ios::ate
追加方式写文件 ios::app
如果文件存在,先删除,再创建 ios::trunc
二进制方式 ios::binary