黑马程序员C++自学笔记

C++的学习开始
编译器的下载(Visual Studio)
https://visualstudio.microsoft.com/zh-hans/
hello world案例的实现
#include <iostream>
using namespace std;

int main()
{
	cout << "hello world" << endl;
	system("pause");
	return 0;
}
基础语法
注释

概述:用于解释说明代码,方便自己与其他程序员理解代码,被注释的内容不会被编译

单行注释
//内容

多行注释
/*
内容
*/
变量

概述:给一段指定内存空间起名,方便操作这段内存

语法:数据类型 变量名 = 初始值;

常量

概述:用于记录程序中不可更改的数据

C++定义常量的两种方式

  • #define 宏常量:#define 常量名 常量值
    • 通常在文件上方定义,表示一个常量
  • const修饰的变量:const 数据类型 常量名 = 常量值
    • 通常在变量定义前加关键字const,修饰该变量为常量,不可修改
关键字

概述:关键字是C++中预先保留的单词(标识符)

  • 在定义变量或者常量时候,不要用关键字
标识符命名规则

概述:C++规定给标识符(变量,常量)命名时,有一套自己的规则

  • 标识符不能是关键字
  • 标识符只能由字母、数字、下划线组成
  • 第一个字符必须为字母或下划线
  • 标识符中字母区分大小写

建议:给标识符命名时,争取做到命名知意的效果,方便别人和其他人阅读

数据类型

概述:C++规定在创建一个变量或者常量时,必须要指定出相应的数据类型,否者如法给变量分配内存

数据类型占用空间取值范围
short(短整型)2字节-(215~215-1)
int(整型)4字节-(231~231-1)
long(长整型)Windows为4字节,linux为4字节(32位),8字节(64位)-(231~231-1)
long long(长长整型)8字节-(263~263-1)
float(单精度)4字节7位有效数字
double(双精度)8字节15~16位有效数字
char(字符型)1字节一个字符
bool(布尔型)1字节true or false
sizeof关键字

概述:利用sizeof关键字可以统计数据类型所占用内存大小

语法:sizeoff(数据类型/变量)

转义字符

概述:表示一些不能显示出来的ASCII字符

转义字符含义ASCII码值(十进制)
\a警报007
\b退格(bs),将当前位置移到前一列008
\f换页(ff),将当前位置移到下页开头012
\n换行(lf),将当前位置移到下一行开头010
\r回车(cr),将当前位置移到本行开头013
\t水平制表(ht)(跳到下一个tab位置)009
\v垂直制表(vt)011
\ \代表一个反斜线字符“\”092
\ ’代表一个单引号(撇号)字符039
\ ’代表034
?代表063
\0数字0000
\ddd8进制转义字符,d范围0-73位八进制
\xhh16进制转义字符,h范围0-9,a-f3位16进制
字符串型

概述:用于表示一个字符串

两种风格

  • C风格字符串:char 变量名[] = “字符串值”

注意:C风格的字符串要用双引号括起来

  • C++风格字符串:string 变量名 = “字符串值”

注意:C++风格字符串,需要加入头文件#include

数据的输入

概述:从键盘获取数据

关键字:cin

语法:cin >> 变量;

逻辑运算符

概述:根据表达式的值返回真值或假值

逻辑运算符有一下符号:

运算符术语示例结果
!a如果a为假,则!a为真;否则反之
&&a&&b如果a和b都为真,则结果为真,否则为假
||a||b如果a和b有一个为真,则结果为真,二者都为假时,结果为假。
位运算符

概述:位运算符主要用于二进制的位运算,并逐位执行操作

  • 与 &(同为1,则为1)
  • 或 |(有1,则为1)
  • 异或 ^(不同则为1)
  • 取反 ~(1为0,0为1)
选择结构

概述:依据条件是否满足,有选择的执行相应的功能

if语句三种形式

//单行格式if语句
if(条件){
	条件满足执行语句
}

//多行格式if语句
if(条件){
	条件满足执行语句
}else{
	条件不满足执行语句
}

//多条件的if语句
if(条件1){
	条件1满足执行语句
}else if(条件2){
	条件2满足执行语句
}else{
	都不满足执行语句
}

注意:if语句可以嵌套

三目运算符

语法:表达式1 ? 表达式2 :表达式3

解释:

如果表达式1的值为真,执行表达式2,并返回表达式2的结果

如果表达式1的值为假,执行表达式3,并返回表达式3的结果

switch语句

switch(){
	case 结果1:执行语句;break;
	case 结果2:执行语句;break;
	……
	default:执行语句;break;
}
循环结构

概述:依据条件是否满足,循环多次执行某段代码

while循环语句

概述:满足循环条件,执行循环语句

语法:while(循环条件){循环语句}

do……while循环语句

概述:满足循环条件,执行循环语句

语法:do{循环语句}while(循环条件);

注意:与while的区别在于do……while会先执行一次循环语句,再判断循环条件

for循环语句

概述:满足循环条件,执行循环语句

语法:for(起始表达式;条件表达式;末尾循环体){循环语句;}

rand语句

概述:生成一定范围的随机数

示例:int num = rand()%100 +1; //生成0+1~99+1之间的随机数

break语句

概述:用于跳出选择结构或者循环结构

break使用时机:

  • 出现在switch条件语句中,作用是终止case并跳出switch
  • 出现在循环语句中,作用是跳出当前的循环语句
  • 出现在嵌套循环中,跳出最近的内层循环语句
continue语句

概述:在循环语句中,跳过本次循环中余下尚未执行的语句,继续进行下一次循环

注意:continue并没有使整个循环终止,而break会跳出循环

goto语句

概述:如果标记的名称存在,执行到goto语句时,会跳转到标记的位置

语法:goto 标记;

注意:在程序中不建议使用goto语句,以免造成程序流程混乱

数组

概述:所谓数组,就是一个集合,里面存放了相同类型的数据元素,且每个数据元素都是相同的数据类型,数组的内存位置组成也是连续的

一维数组定义的三种方式:

数据类型 数组名 [数组长度];

数据类型 数组名[数组长度] = {值1,值2,……};

数据类型 数组名[] = {值1,值2,……};

二维数组定义的四种方式:

数据类型 数组名 [行数] [列数];

数据类型 数组名 [行数] [列数] = {{数据1,数据2},{数据3,数据4}};

数据类型 数组名 [行数] [列数] = {数据1,数据2,数据3 ,数据4};

数据类型 数组名 [ ] [列数]= {数据1,数据2,数据3 ,数据4};

函数

概述:将一段经常使用的代码封装起来,减少重复代码

语法

//函数的声明
返回值类型 函数名 (形参);


//函数的定义
返回值类型 函数名 (形参)
{
	函数体语句
	return表达式
}

//函数的调用
函数名 (实参);

注意:所谓值传递,就是函数调用时将实参数值传入形参,如果形参发生变化,并不会影响实参,但是地址传递的形参发生变化,实参也会发生变化(如果不想修改实参,就用值传递,如果想修改实参,就用地址传递)
函数的分文件编写

作用:让代码结构更加清晰

函数份文件编写一般有4个步骤

  1. 创建后缀名为.h的头文件
  2. 创建后缀名为.cpp的源文件
  3. 在头文件中写函数的声明
  4. 在源文件中写函数的定义
指针

概述:可以通过指针间接访问内存,内存编号是从0开始记录的,一般用十六进制数字表示,可以利用指针变量保存地址

语法:数据类型 *变量名;

注意:在32位操作系统下,指针占4个字节,在64位操作系统下,指针占8个字节,跟数据类型无关。

空指针和野指针

空指针:指针变量指向内存编号为0的空间(如:int *p = NULL)

野指针:指针变量指向非法的内存空间(如:int *p = (int *)0x1100)

用途:初始化指针变量

注意:空指针指向的内存是不可以访问的(0-255为系统占用内存,不允许用户访问),且空指针与野指针都不是我们申请的空间,因此不要访问。

const修饰指针(指针常量与常量指针)

const修饰指针有三种情况:

  • const修饰指针 --常量指针
  • const修饰常量 --指针常量
  • const即修饰指针,又修饰常量

常量指针与指针常量性质的记忆方法:谁先说谁不变,谁先说谁先写

示例:

//const修饰的是指针,指针指向可以改,指针指向的值不能改
const int p1 = &a;
p1 = &b;  //正确
*p1 = 100;//报错

//const修饰的是常量,指针指向不可以改,指针指向的值可以改
const int p2 = &a;
p2 = &b;  //报错
*p2 = 100;//正确

//const即修饰指针又修饰常量
const int p3 = &a;
p3 = &b;  //报错
*p3 = 100;//报错

结构体

概述:结构体属于用户自定义的数据类型,允许用户存储不同数据类型

结构体定义和使用

语法:struct 结构体名 {结构体成员列表};

通过结构体创建变量的方式有三种:

  • struct 结构体名 变量名
  • struct 结构体名 变量名 = {成员1值,成员2值……}
  • 定义结构体时顺便创建变量

总结:

  • 定义结构体时的关键字是struct,不可省略
  • 创建结构体变量时,关键字struct可以省略
  • 结构体变量利用操作符"."访问成员
结构体数组

概述:将自定义的结构体放入到数组中方便维护

语法:struct 结构体名 数组名[元素个数] = { { },{ }, ... { } }

结构体指针

作用:通过指针访问结构体中的成员

  • 利用操作符->可以通过结构体指针访问结构体属性
结构体嵌套结构体

概述:结构体中的成员可以是另一个结构体

总结:在结构体中可以定义另一个结构体作为成员,用来解决实际问题

结构体做函数参数

概述:将结构体作为参数向函数中传递

结构体中const使用场景

概述:用const来防止误操作

内存分区模型

C++程序在执行中,将内存大方向化为四个区域

  • 代码区:存放函数体的二进制代码,由操作系统进行管理的
  • 全局区:存放全局变量和静态变量以及常量
  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

内存四区意义:不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程

程序运行前

在程序编译后,生成了exe可执行程序,未执行该程序前分为了两个区域

代码区:

  • 存放cpu执行的机器指令
  • 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
  • 代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令

全局区:

  • 全局变量和静态变量存放在此
  • 全局区还包括常量区,字符串常量和其他常量也存放在此
  • 该区域的数据在程序结束后由操作系统释放

总结:

  • C++中在程序运行前分为全局区和代码区
  • 代码区特点是共享和只读
  • 全局区存放全局变量,静态变量(由static关键字修饰),常量
  • 常量区中存放const修饰的全局常量和字符串常量
程序运行后

栈区:

  • 由编译器自动分配释放,存放函数的参数值,局部变量等
  • 注意:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

堆区:

  • 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
  • 在C++中主要利用new在堆区开辟内存
new操作符

C++中利用new操作符在堆区开辟数据

堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete

语法:new 数据类型

利用new创建的数据,会返回该数据对应的类型的指针

注意:自定义数据类型(如:结构体,数组)要用delete[]释放

引用
引用的基本使用

作用:给变量起别名

语法:数据类型 &别名 = 原名

引用的注意事项
  • 引用必须初始化
  • 引用在初始化后,不可以更改
引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参

优点:可以简化指针修改实参

引用做函数的返回值

作用:引用是可以作为函数的返回值存在的

注意:不要返回局部变量引用

用法:函数调用作为左值

示例:

//返回局部变量引用
int& test01(){
	int a = 10;//局部变量
	return a;
}
//返回静态变量引用
int& test02(){
	static int a = 20;
	return a;
}
int main(){
	//不能返回局部变量的引用
	int& ref = test01();
	cout <<"ref ="<< ref <<enl;//10
	cout <<"ref ="<< ref <<enl;//乱码
	
	//如果函数做左值,那么必须返回引用
	int& ref2 = test02
	cout <<"ref2 ="<< re2f <<enl;//20
	cout <<"ref2 ="<< ref2 <<enl;//20
	test02() = 1000;
	cout <<"ref2 ="<< re2f <<enl;//1000
	cout <<"ref2 ="<< ref2 <<enl;//1000
	system("pause");
	return 0;
}
引用的本质

本质:引用的本质在C++内部实现是一个指针常量

示例:

//发现是引用,转换为 int* const ref = &a;
void func(int& ref){
	ref = 100;//ref是引用,转换为*ref = 100
}
int main(){
	int a = 10;
	//自动转换为int* const ref = &a;指针常量是指针指向不可改,也说明为什么引用不可更改
	int& ref = a;
	ref = 20;//内部发现ref是引用,自动帮我们转换为:*ref = 20;
	cout << "a:" << a << endl;
	cout << "ref:" << ref << endl;
	func(a);
	return 0;
}
常量引用

作用:常量引用主要用来修饰形参的,防止误操作

在函数形参列表中,可以加const修饰形参,防止形参改变实参

示例:

//引用使用的场景,通常用来修斯形参
void showValue(const int& v){
	//v += 10;
	cont << v << endl;
}
int main(){
	//int& ref = 10; 引用本身需要一个合法的内存空间,因此这行错误
	//加入const就可以了,编译器优化代码,int,temp = 10;const int& ref = temp;
    const int& ref = 10;
    //ref = 100;//加入const后不可以改变变量
    const << ref << endl;
    //函数中利用常量引用防止误操作修改实参
    int a = 10;
    showValue(a);
    system("pause");
    return 0;
}
函数提高
函数默认参数

概述:在C++中,函数的形参列表中的形参是可以有默认值的。

语法:返回值类型 函数名 (参数 = 默认值){}

注意:

  • 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
  • 如果函数声明有默认值,函数实现的时候就不能有默认参数
函数占位参数

概述:C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置

语法:返回值类型 函数名 (数据类型){}

示例:

//函数占位参数,占位参数也可以有默认参数
void func(int a,int = 10){
	cont << "this is func" << endl;
}
int main(){
	func(10,10);//占位参数必须填补
	system("pause");
}
函数重载
函数重载概述

作用:函数名可以相同,提高复用性

函数重载满足条件:

  • 同一个作用域下
  • 函数名相同
  • 函数参数的类型不同,或者个数不同,或者顺序不同

注意:函数的返回值不可以作为函数重载的条件

函数重载注意事项
  • 引用可以作为重载条件
  • 函数重载碰到函数默认参数(碰到默认参数产生歧义而报错,需要避免)
类和对象

概述:C++面向对象的三大特性为:封装,继承,多态,C++认为万物都皆有对象,对象上有其属性和行为,简而言之,具有相同性质的对象,我们可以抽象称为

封装的意义

意义:

  • 将属性和行为作为一个整体,表现在生活中的事物
  • 将属性和行为加以权限控制

语法:class 类名{访问权限:属性/行为};

属性即成员变量,行为即成员函数

访问权限有三种:

  • public 公共权限
    • 类内可以访问,类外可以访问
  • protected 保护权限
    • 类内可以访问,类外不可以访问,子类可以访问父类中的保护内容
  • private 私有权限
    • 类内可以访问,类外不可以访问,子类不可以访问父类中的私有内容
struct和class区别

概述:在C++中struct和class唯一的区别就在于默认的访问权限不同

区别:

  • struct 默认权限为公共
  • class 默认权限为私有
成员属性设置为私有

优点:

  • 将所有成员属性设置为私有,可以自己控制读写权限
  • 对于写权限,我们可以检测数据的有效性
构造函数和析构函数

构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用。

语法:类名(){}

  • 构造函数,没有返回值也不写void
  • 函数名称与类名相同
  • 构造函数可以有参数,因此可以发生重载
  • 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

语法:~类名(){}

  • 析构函数,没有返回值也不写viod
  • 函数名称与类名相同,在名称前面加上符号~
  • 析构函数不可以有参数,因此不可能发生重载
  • 程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
构造函数的分类及调用

两种分类方式:

  • 按参数分为:有参构造和无参构造
  • 按类型分为:普通构造和拷贝构造

三种调用:

  • 括号法
  • 显示法
  • 隐式转换法

示例:

//1.构造函数分类
//按照参数分类 有参和无参构造 无参又称为默认构造函数
//按照类型分为 普通构造和拷贝构造
class preson{
public:
	//无参(默认)构造函数
	preson(){
		cout << "无参构造函数!" << endl;
	}
	//有参构造函数
	person(int a){
		age = a;
		cout << "有参构造函数!" << endl;
	}
	//拷贝构造函数
	person(const person& p){
		age = p.age;
		cout << "拷贝构造函数!" << endl;
	}
	//析构函数
	~person(){
		cout << "析构函数!" << endl;
	}
public:
	int age;
};
//2.构造函数的调用
//调用无参构造函数
void test01(){
	person p;
}
//调用有参的构造函数
void test02(){
	//括号法
	person p1(10);
	//注意:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
	//person p2();
	
	//显示法
	person p2 = person(10);
	person p3 = person(p2);
	//person(10)单独写就是匿名对象 当前行结束之后,马上析构
	
	//隐式转换法
	person p4 = 10;//person p4 = person(10);
	person p5 = p4;//person p5 = person(p4);
	//注意:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
    //person p5(p4)
}
int main(){
	test01();
	test02();
	system("pause");
	return 0;
}

拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常有三种情况

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值的方式返回局部对象
构造函数调用规则

概述:默认情况下,C++编译器至少给一个类添加3个函数

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认拷贝构造函数,对属性进行拷贝

规则如下:

  • 如果用户定义有参构造函数,C++不在提供默认无参构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,C++不会提供其他的构造函数
深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

初始化列表

概述:C++提供了初始化列表语法,用来初始化属性

语法:构造函数():属性1(值1),属性2(值2)…{}

类对象作为类的成员

概述:C++类中的成员可以是另个类的对象,我们称为该成员为对象成员

列如:

class A {}
class B
{
	A a;
}
//B类中有对象A作为成员,A为对象成员

注意:

  • 当类中成员是其他类对象时,我们称该成员为 对象成员
  • 构造的顺序是:先调用对象成员的构造,再调用本类的构造
  • 析构顺序与构造相反(口诀:先有零件再装机,先拆机再扔零件)
静态成员

概述:静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员

静态成员分为:

  • 静态成员变量
    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  • 静态成员函数
    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量

静态成员变量特点

  • 在编译阶段分配内存
  • 类内声明,类外初始化
  • 所有对象共享同一份数据

静态成员变量两种访问方式

  • 通过对象(对象.成员变量)
  • 通过类名(类名::成员变量)

注意:静态成员变量也是有访问权限的

静态成员函数特点

  • 程序共享一个函数
  • 静态成员函数只能访问静态成员变量

静态成员函数的访问跟静态成员变量的访问相差无几

成员变量和成员函数分开存储

概述:C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上

结论:

  • 只有非静态成员变量占对象空间
  • 静态成员变量不占用对象空间
  • 函数也不占用对象空间,所有函数共享一个函数实例
this指针概念

概述:C++通过特殊的对象指针,this指针可以区分那个对象调用自己的。this指针指向被调用的成员函数所属的对象,this指针是隐含每个非静态成员函数内的一种指针,不需要被定义,直接可以使用

用途:

  • 当形参和成员变量同名时,可以用this指针来区别
  • 在类的非静态成员函数中返回对象本身,可以使用return *this
空指针访问成员函数

概述:C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针

如果用到了this指针,需要加以判断,保证代码的健壮性

const修饰成员函数

常函数:

  • 成员函数后加const后我们称为这个函数为常函数(类型 函数名(形参) const {})
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

  • 声明对象前加const称该对象为常对象

  • 常对象只能调用常函数

友元

概述:在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就可以要用到有元的技术,友元的目的就是让一个函数或者类访问另一个类中的私有成员

关键字:friend

友元的三种实现方法

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元
运算符重载

概述:对已有的运算符重新进行重新定义,赋予其另一种功能,以适应不同的数据类型

编译器通用函数名:operator运算符

本节内容有(C++运算符重载_哔哩哔哩_bilibili

  • 加号运算符重载
  • 左移运算符重载
  • 递增运算符重载
  • 赋值运算符重载
  • 关系运算符重载
  • 函数调用运算符重载

示例:

//只能利用全局函数重载左移运算符
ostrean & operator<<(ostream &cout,Person &p)//本质 operator<< (cout,p) 简化 cout << p
{
	cout << "m_A =" << p.m_A << " m_B = " << p.m_B;
	return cout;
}
继承的基础语法

语法:class A:继承方式 B;

继承的好处:可以减少重复的代码

A类称为子类 或 派生类

B类称为父类 或 基类

派生类中的成员,包含两个部分:

  • 一类是从基类继承过来的,一类是自己增加的成员
  • 从基类继承过过来的表现其共性,而新增的成员体现了其个性
继承方式

继承方式一共有三种:

  • 公共继承(父类的公共成员 到子类中还是公共成员,父类的保护成员 到子类中还是保护成员)
  • 保护继承(父类的公共成员 到子类中变为保护成员,父类的保护成员 到子类中还是保护成员)
  • 私有继承(父类的公共成员 到子类中变为私有成员,父类的保护成员 到子类中变为私有成员)

注意:父类中的私有成员也是被子类继承下去了,只是由编译器给隐藏后 访问不到

继承中构造和析构顺序

省流:父构->子构- >子析->父析

继承同名成员处理

问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或者父类中同名的数据呢?

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域(父类名称::)
  • 当子类与父类拥有同名的成员函数,子类会隐藏父类中的同名成员函数,加作用域可以访问到父类中的同名函数

注意:同名静态成员处理方式和非静态处理方式一样,只不过静态成员有两种访问的方式(通过对象 和 通过类名)

多继承语法

概述:C++允许一个类继承多个类

语法:class 子类:继承方式 父类1,继承方式 父类2……

注意:多继承可能引发父类中的同名成员出现,需要加作用域区分,且C++实际开发中不建议用多继承

菱形继承

概述:两个派生类继承同一个基类,又有某个类同时继承这两个派生类,这种继承被称为菱形继承,或者钻石继承

问题:菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费,利用虚继承可以解决问题

示例

class animal
{
public:
	int m_age;
};
//继承前加关键字virtual后,变为虚继承
//此时公共的父类animal称为虚基类
class sheep : virtual public animal {};
class tuo   : virtual public animal {};
class sheeptuo : public sheep,public tuo {};
多态的基本概述

概述:多态是C++面向对象三大特征之一

多态分为两类

  • 静态多态:函数重载 和 运算符重载属于静态多态,复用函数名
  • 动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态区别

  • 静态多态的函数地址早绑定 - 编译阶段确定函数地址
  • 动态多态的函数地址晚绑定 - 运行阶段确定函数地址

多态满足条件

  • 有继承关系
  • 子类重写父类中的虚函数(用关键字virtual修饰)

多态使用条件

  • 父类指针或引用指向子类对象

重写:函数返回值类型 函数名 参数列表 完全一致称为重写

纯虚函数和抽象类

概述:在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数

纯虚函数语法:virtual 返回值类型 函数名(参数列表)=0;

当类中有了纯虚函数,这个类也被称为抽象类

抽象类特点:

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方法:将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构共性:

  • 可以解决父类指针释放子类对象
  • 都需要有具体的函数实现

虚析构和纯虚析构区别:

  • 如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法:virtual ~类名(){}

纯虚析构语法:

  • virtual ~类名() = 0;
  • 类名::~类名(){}

注意:

  • 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
  • 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
  • 拥有纯虚析构函数的类也属于抽象类
文件操作

文件可以将数据持久化,C++中对文件操作需要包含头文件

文件类型分为两种:

  1. 文本文件 -文件以文本的ASCII码形式存储在计算中
  2. 二进制文件 -文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们

操作文件的三大类

  • ofstream:写操作
  • ifstream:读操作
  • fstream:读写操作
写文件

写文件步骤如下:

  • 包含头文件
    • #include
  • 创建流对象
    • ofstream ofs;
  • 打开文件
    • ofs.open(“文件路径”,打开方式);
  • 写数据
    • ofs << “写入的数据”;
  • 关键文件
    • ofs.close();

文件打开方式:

打开方式解释
ios::in为读文件而打开文件
ios::out为写文件而打开文件
ios::ate初始位置:文件尾
ios::app追加方式写文件
ios::trunc如果文件存在先删除,再创建
ios::binary二进制方式

注意:文件打开方式可以配合使用,利用|操作符

例如:用二进制方式写文件ios::binary|ios::out

总结:

  • 文件操作必须包含头文件fstream
  • 读文件可以利用ofstream,或者fstream类
  • 打开文件时候需要指定操作文件路径,以及打开方式
  • 利用<<可以向文件中写数据
  • 操作完毕,要关闭文件
读文件

读文件步骤如下:

  • 包含头文件
    • #include
  • 创建流对象
    • ifstream ifs;
  • 打开文件并判断文件是否打开成功
    • ifs.open(“文件路径”,打开方式);
  • 读数据
    • 四种方式读取
  • 关键文件
    • ifs.close();

示例:

//第一种方式
char bur[1024]={0}
while (ifs >> buf)
{
	cout << buf << endl;
}
//第二种
char buf[1024]={0};
while (ifs.getline(buf,sizeof(buf)))
{
	cout << buf << endl;
}
//第三种
string buf;
while (getline(ifs,buf))
{
	cout << buf << endl;
}
//第四种
char c;
while ((c = ifs.get()) != EOF)
{
	cout << c;
}

总结:

  • 读文件可以利用ifstream,或者fstream类
  • 利用is_open函数可以判断文件是否打开成功
  • close关闭文件
写二进制文件

以二进制的方式对文件进行读写操作,打开方式要指定为ios::binary

二进制方式写文件主要利用流对象调用成员函数write

函数原型:ostream& write(const char * buffer,int len);

参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

读二进制文件

二进制方式读文件主要利用流对象调用成员函数read

函数原型:istream& read(char * buffer,int len);

参数解释:字符指针buffer指向内存中的一段存储空间。len是读写的字节数

笔记补充

如果看到这里还没有能解决你的疑问,那么你应该具有了一定的相当的编程基础,这个时候的你已经有能力只通过文档就能学好编程的能力了,这里我推荐一个C++的文档,希望你能解决你的疑问。

C++ 教程 | 菜鸟教程 (runoob.com)

  • 29
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值