C++学习笔记

C++学习笔记

一、 编译环境及基本操作:
1.新建项目-win32控制台应用程序-空项目
2.代码区
3.解决方案解析
project7_17_38:项目目录(一个解决方案可以有多个项目(右键-设为启动项目))
.sdf:分析文件(每次打开项目时创建,可以删除)
.sln:解决方案(打开项目,不可删除)
.suo:用户设置(可以删除)
main:主函数,也称为入口函数
一个项目只有1个主函数
一个解决方案可以有多个主函数
编译:查找是否有语法错误,生成.obj(目标)文件,
生成-编译(Ctrl+F7)
输出窗口:查看错误原因。
运行:查看最终效果,将.obj文件链接成.exe,
调试-开始执行(Ctrl+F5)
引用头文件:
#include “文件名”(引用自定义文件):在项目目录下查找该文件,找不到
再到系统目录下查找,再找不到就报错。
#include <文件名>(引用系统文件):在系统目录下查找该文件,找不到就报错。
注释:不参与编译,方便程序员阅读代码。(70%)
单行注释:以回车结束
多行注释:/内容/ 快捷键:Ctrl+K+C
取消注释:Ctrl+K+U
例:
/作者:**
日期:2018/7/17
版本:1.0.0*/
/*
常量:程序中不能改变的量。1,3.1,‘a’(字符),“abc”(字符串)
变量:程序中可以改变的量,比如血量/等级/经验值等。
定义:数据类型 变量名;
变量名:合法的标识符。
1.由字母(a-z/A-Z)/数字(0-9)/下划线(_)其中至少一者组成。
例如:a,a3,a_3
2.开头不能是数字 比如3a(错误)
3.不能是关键字 比如:INt Int iNt(C++区分大小写)
4.人为规定:顾名思义(hp/lv/exp/hero_lv)
二、 数据类型:
整型/浮点型/字符型/布尔型
1.整型(用来表示整数):
int(普通整型):
在16位机器上占2个字节,
在32位以上机器上占4个字节。
short(短整型):占2个字节。
long(长整型):占4个字节。
long long(扩展性整型):占8个字节。
内存:以字节为单位(变量占内存)
机器码:
以二进制(0和1)存储。
一个字节用8位二进制表示,
第一位表示符号位(0表示正,1表示负)
short(-2^15 ~ 2^15-1):最大值二进制:01111111 11111111
=10000000 00000000 - 1 = 2^15 - 1
最小值:-2^15

2.进制转换
二进制转十进制:从右往左,每一位上乘以2的n次方,
n从0开始,依次递增1,求和。
十进制转二进制:依次除以2,直到商为0,余数从下往上读。
m进制转十进制:从右往左,每一位上乘以m的n次方,
n从0开始,依次递增1,求和。
十进制转m进制:依次除以m,直到商为0,余数从下往上读。
二进制与八进制:每3位二进制对应1位八进制。
二进制与十六进制:每4位二进制对应1位十六进制。
程序:十进制:(0-9)
八进制:(0-7):017 024
十六进制:(0-9,a-f(A-F)):0xab 0x1a

3.浮点型(用来表示小数默认为double类型):
float(单精度):占8个字节
double(双精度):占8个字节
long double(扩展性精度):占16个字节
2.%m.nf:n表示小数点位数
m表示字符宽度
如果实际字符宽度大于m,则正常显示
如果小于,m为正数,在前面补空格,m为负数,在后面补空格
3.浮点数的表示形式:1.小数形式
2.指数形式 3e2=3*10^2
4.字符型:char:占一个字节。
既可以表示小整数(-128-127),又可以表示字符
每一个字符都对应一个整数,称为ASCLL码值。
1.字符类型在参与运算时,提升为整型
‘A’-‘C’=-3;
2.大小写切换:大写-小写:ch+(‘a-‘A’)
3数字与字符的切换:0=‘o’:num+(‘o=0’)
4.转义字符:\字符:占1个字节
\八进制:\0
\十六进制:\x
5布尔类型(用来表示真假):
bool:占1个字节(true(1)和false(0))
1.布尔类型参与运算时,提升为整型
2.一切非零的数都为真
#include 输入输出流库
using namespace std;std是命名空间名
引用命名空间
1.全局引用(可能会造成命名空间污染)
(当前文件所有对象都可以使用)
2.局部引用:命名空间::
c语言的输入输出:scanf(),printf()
c++的输入输出:cin>>,cout<<

三、 运算符:

  1. 运算符运算法则:
    算术运算符:+ - * / %(求余/取模)
    1.不同精度的类型参与运算时,结果以精度高的为准。
    int + double = double
    2./:整型相除时,结果依然为整型。3/2 = 1
    3.%:运算符左右都必须为整型。
    判断偶数/奇数:x%2
    拆分数字:198
    个位:198/1%10 = 8
    十位:198/10%10 = 9
    百位:198/100%10 = 1
    个位/十位/百位:a,b,c (c100+b10+c)
    棋牌:card
    1.花色:card / 0x10
    2.点数:card % 0x10
    红桃3:0x13
    (花色:
    红桃(0x11-0x1D)/黑桃(0x21-0x2D)/方块(0x31-0x3D)/梅花(0x41-0x4D))
    2.点数:1-13
    3.大小王(0x51,0x52)
    红桃3:0x13)
    自增自减运算符:
    ++:变量本身值+1;a++
    –:变量本身值-1;a–
    前置:++a:先自增再运算。
    后置:a++:先运算再自增。
    1.优先级:后置>前置
    2.效率:前置>后置
    关系运算符(结果为bool类型):> < >= <= ==(等于) !=(不等于)
    逻辑运算符(式子左右为bool类型,结果为bool类型):(and or not)
    逻辑&&(且):a&&b;当a和b同时为真时,结果为真,否则为假。
    当a表达式为假时,则不计算b表达式。
    逻辑或||:a||b;当a和b同时为假时,结果为假,否则为真。
    当a表达式为真时,则不需要计算b表达式。
    逻辑非!:!a:a为真,!a为假,a为假,!a为真
    赋值运算符:=
    1.左边必须为变量
    2.优先级倒数第二
    3.与其他运算符结合使用:+= -= *= /= %=
    a+=1;a=a+1;
    a-=b;a=a-b;
    4.当表达式中有多个赋值运算符时,从右往左进行运算。

逗号运算符:表达式1,表达式2,…表达式n
1.逗号运算符的优先级最低
2.运算法则从左往右,最终结果为最后一个表达式的结果。

条件运算符(三目运算符):
?: a?b:c;当a为真时,执行b,否则执行c。

强转:强制类型转换
(要转换的类型)
(int)量;

位运算符(速度最快):以二进制的补码参与运算。
原码:将一个数转化为二进制。
反码:正数的反码是本身,负数的反码是除
符号位外全部取反(0变1,1变0)
补码:正数的补码是本身,负数的补码是
反码+1.
补码-原码:补码的补码就是原码。
运算流程:原码-补码(参与运算)-补码(运算结果)-原码(最终结果)
<<(左移):a<<1;a左移1位;a<<b;a左移b位;
将二进制的补码整体往左移n位,后面补0.(乘以2的n次方)

(右移):a>>1;
将二进制的补码整体往右移n位,正数补0,负数补1.

按位与&:a&b;(消除某些位数&0=0/保留某些位数&1)
将a和b的补码从右往左一一对应,同1为1,否则为0.
按位或|:a|b;
将a和b的补码从右往左一一对应,有1为1,否则为0.
按位异或:ab;
将a和b的补码从右往左一一对应,相同为0,不同为1.
按位非a
将a的补码全部取反(0变1,1变0)
1.实现交换两个数的值:
1.第三方变量
2.加法
3.异或
2.不使用算术运算符,如何判断一个整数是奇数还是偶数?
(x&1) == 0?printf(“偶数”):printf(“奇数”);
3.不使用关系运算符,如何判断一个整数是正数还是负数?
x>>31?printf(“负数等于-1”):printf(“正数等于0”);
4.如何判断一个数二进制的第n位是1还是0?
(x>>(n-1))&1
x&(1<<(n-1))
2. 运算符结合性

  1. 运算符优先级
    优先级:单目>双目>三目(运算符左右的式子个数)
    算术运算符>(<< >>)>关系运算符>位运算符(&>^>|)

逻辑运算符(&&>||)>条件运算符>赋值运算符>逗号运算符

四、 语 句:
条件语句和循环语句
1、条件语句:
1.if语句:
if(条件1) 如果条件1为真,则执行语句1
{
语句1;简单语句:只有一条语句,可以省略{}
复合语句:有多条语句,不可以省略{}
}
else当条件1为假时,执行语句2
{
语句2;
}

if(条件1) 如果条件1为真,则执行语句1
{
语句1;简单语句:只有一条语句,可以省略{}
复合语句:有多条语句,不可以省略{}
}
else if(条件2) 如果条件2为真时,执行语句2
{
语句2;
}else 条件1和条件2都不满足时,执行语句3
{
语句3;
}
if语句实例:

if(s>0&&s<=10)
	if(s>=3&&s<=6)  3 4 5 6 
		x=2;
	else if(s>1||s>8)  2 7 8 9 10
		x=3;
	else  1
		x=1;
else 
    x=0;

1.条件语句只执行一次
2.elseif和else根据情况可写可不写,
elseif可以无限次添加,但else只有一个。
3.if语句只会进入一个分支。
4.else匹配原则:与之前最近的没有匹配else的if匹配。

表达式:由常量/变量和运算符其中至少一种组成。
比如:a/a+3/3+2/3/a+b
常量表达式:由常量和运算符组成的式子。
1.计算表达式的值
2.与case后的常量表达式一一比较,如果相同,则执行case后的语句。
2.Switch(表达式)语句:
Switch(表达式)
{
Case 常量表达式1;
语句1;
Case 常量表达式2;
语句2;
Case 常量表达式n;
语句n;
}{}不能省略
注意:
1.case后的常量表达式不能相同
2.结束的标志:1.}2.berak(跳出switch)
3.default与位置无关
实例:
输入一个成绩,判断在0-59不及格–0-5
60-79:良好 --6 7
80以上:优秀 - 8-10

int grade;
	cin >> grade;
	switch (grade / 10)
	{
	case 6:
	case 7:6/7表达式执行相同语句
		cout << "良好" << endl;
		break;
	case 8:
	case 9:
	case 10:
		cout << "优秀" << endl;
		break;
	default:0-5
		cout << "不及格" << endl;
		break;
	}

2、循环语句(可能执行多次):
1.for循环:
for(表达1;表达2;表达3)当所有表达式都省略时两个分号都不能省略
{
循环体;循环体是简单语句时可省略大括号{}不写
}
表达式1:用来给循环变量初始化;int i=0; (可以省略)
表达式2:判断循环跳出的条件;(可以省略,但要加入转移语句,例如:break)
当条件为真,执行循环体,为假,跳出for循环。
表达式3:改变循环变量的值(步长);++i;i+=2;(可以省略)
For循环流程:

转移语句:
Break:
1.在switch中,跳出switch语句
2.在循环语句中,跳出当前这一层循环;
Continue:跳出当前这一次循环,执行下一次循环
实例:
//99乘法表
//1
1=1 。。。。 19=9
//2
1=2 22=4
//3
1=3 32=6 33=9
//

for (int i = 1; i <= 9; i++)
	{
		//打印一行
	for (int j = 1; j <= i; j++)
	{
		cout << i <<"*" << j << "=" << i * j << "\t";
		}
	//换行
	cout << endl;
}

2.While循环:
1.判断表达式真假2当表达式为真时,执行循环体,否则跳出循环(反复执行1.2)
While(表达式)
{
循环体;
}
实例:
int main()
{
//输入一个整数,从个位起,输出每一位上的数
//求逆数:1786-6871
//1786 //6871 =68710+1
//178 //687 =68
10+7
//17 //68 = 6*10+8
//1 //6

int num,newNum = 0;
	cin >> num;
	while (num != 0)
	{
		//cout << num % 10 << endl;//负数求余:余数符号:被除数符号决定//值:绝对值求余 
		newNum = newNum*10 + num % 10;     //-2 % 3 = -2   - 2 % -3 = -2;
		num /= 10;
	}
	cout << newNum << endl;

3.do…while语句:
1执行一次循环体
2.判断表达式的值
3.如果为真,则执行循环体,否则跳出循环体
4.反复2和3
do
{
循环体;简单和复合都不能省略

}while(表达式);({}和;都不能省略)
实例:

int main()
{
	//随机种子:只需要写一次
	srand(time(NULL));
	//9*9乘法表
	int i = 1;
	do 
	{
		int j = 1;
		do
		{
			cout << i <<"*" << j << "=" << i * j << "\t";
			j++;
		} while (j <= i);
		i++;
		cout << endl;
	} while (i <= 9);

五、 数 组:
数组的定义:(数组:是一类相同数据类型元素的集合)
数据类型 数组名[数组大小];
数组大小必须为常量表达式:
1.常量与运算符结合的式子
2.const:只读的,修饰变量 const int a;//a是只读的变量
1.必须初始化
2.被const所修饰的变量不能被修改
3.宏定义 #define 宏变量 值
#define size 5 //在使用size的地方全部用5代替
数组的内存:数组大小*每一个元素的所占字节
数组的内存是连续的。如下图所示:
0 1 2 3 4 8 12

数组的初始化:{}

int n[5] = {1,2,3,4,5};

1.{},所有元素默认为0
2.{1,2,3},给部分元素赋值,其余元素默认为0
3.{1,2,3,4,5},给全部元素赋值,可以省略数组大小不写

数组赋值:给单个元素赋值。
数组元素访问:数组名[下标];
//下标:任意表达式,从0开始,到数组大小-1结束。
n[2];//第三个同学的数学成绩,int类型的变量
n[2]++;
n[2]&&n[3];
n[2] = 3;
*/
示例:

int num[5],count = 0;//count为计数器
	//int count, num[5];
	srand(time(NULL));
	//随机5个数(0-100)存在数组中,计算偶数的个数
	for (int i = 0; i < 5; i++)
	{
		num[i] = rand() % 101;//
		if (num[i] % 2 == 0)
		{
			count++;
		}
	}
(c=getchar())!='\n'//

如何产生4个不同的数字?
1.判断随机的数与之前数组中的数是否相等,
如果相等则重新随机。

for (i = 0; i<4; i++)
	{
		int x = rand() % 10;
		for (j = 0; j<i; j++) //检查是否有重复
		{
			if (num[j] == x)
				break;
		}
		if (j >= i) //没重复
			num[i] = x;
		else
			i--;
	}

2.将10个数放到数组中,随机打乱,取前四个。
1.从键盘输入十个数,存在数组中,
求最大值和所对应的下标。
2.相邻两元素进行比较,将最大值移到最后一个位置。
//数组越界:当数组下标>=数组大小时
//使用非法内存:编译器没有分配给该变量使用的内存
实例:

  int main()
    {
    	 //定义一个数字存放0-9之间的所有数字;
    	 int s[4];//定义数组存放随机打乱的num[]数组的前四个
    	 srand(time(NULL));
    	 //把0-9之间数字放入数组num[]中
    	 int num[10] = {0,1,2,3,4,5,6,7,8,9};
    	 for(int i= 0; i <=9; i++ )
    	 { cout << num[i] << "\t"; }
    	 cout << endl;
    	  //随机打乱数组num[]中的值,给两个随机下标,交换数值;
    	 for (int k = 1; k < 1000; k++)
    	 {		
    		 int num1 = rand() % 10, num2=rand() % 10;
    		 if (num1 != num2)
    {
    			 num[num1] = num[num1]^ num[num2];
    			 num[num2] = num[num1] ^ num[num2];
    			 num[num1] = num[num1] ^ num[num2];
    		 }
    	 }
	 //for (int k = 0; k < 10; k++)//随机打乱数组num[]中的值,给一个随机下标,从第一个数组的值开始交换数值;
	 for (int j = 0; j <4; j++)//取前四个num[]的值并输出
	 {
		 s[j] = num[j];
		 cout << s[j] << "\t" ;
	 }
	 cout << endl; 
	 return 0;
}

1.冒泡排序:(泡往上冒(从小到大)/泡往下冒(从大到小))
1.相邻两元素进行比较,如果前者>后者,则交换两个数,
直到排出这一轮剩下的数中的最大(最小)值。
2.轮数:(数组大小-1)次
3.外层循环是轮数,里层循环是这一轮的比较次数
实例:

for (int i = 1; i <= 9; i++)//控制轮数
	{
		//将相邻两个元素比较,把最大的移动到最后
		for (int j = 0; j <= 9 - i; j++)
		{
			if (num[j] >= num[j + 1])
			{
				num[j] = num[j] ^ num[j + 1];
				num[j + 1] = num[j] ^ num[j + 1];
				num[j] = num[j] ^ num[j + 1];
			}
		}
	}

2.插入排序:(以从小到大为例)
1.将一个数插入到有序数列中,从后往前比,
如果大于某一个值,则位置确定,否则与前面的数交换,继续比较。
2.轮数:把数组看成只有1个数的有序数列,其余数组大小-1个数
当成是插入的数,轮数:数组大小-1.

//外层循环,控制轮数 
	for (int i = 1; i <= 9; i++)
	{   //里层循环
		for (int j = i; j>0; j--)//j初始值:为插入元素的下标
		{
			if (num[j]>=num[j - 1])
			{
				break;//跳出循环
			}
			else
			{
				num[j] = num[j] ^ num[j - 1];
				num[j-1] = num[j] ^ num[j - 1];
				num[j] = num[j] ^ num[j - 1];
			}
		}
	}

3.选择排序: (以从大到小为例)
1.每轮选出当前剩下的数中的最大数,与
第i个元素交换,i从0开始,每次+1.
1 5 2 3 4
第一轮:5 1 2 3 4
第二轮:5 4 2 3 1
第三轮:5 4 3 2 1
第四轮:5 4 3 2 1
2.轮数:数组大小-1
实例:

for (int i = 0; i <= 8; i++)
	{	//里层循环
		int maxi_cur = i;//当前所剩数的最大值下标
		for (int j =i+1; j < 10; j++)
		{
			if (n[j]>n[maxi_cur])
			{
				maxi_cur = j;
			}
		}
		//将最大值与第i个元素交换
		if (maxi_cur != i)
		{
			n[i] = n[i] ^ n[maxi_cur];
			n[maxi_cur] = n[i] ^ n[maxi_cur];
			n[i] = n[i] ^ n[maxi_cur];
		}
	}

4.字符数组: 元素类型为char类型
char 数组名[数组大小];
初始化
1.{}
2.字符串,可以省略{}不写 “abcd”
字符串以’\0’结尾.
输出字符数组名:1.输出字符串,直到’\0’结束

5.二维数组:多个一位数组。
定义:数据类型 数组名[行大小][列大小];
内存:行数列数单个元素所占字节;
二维数组的内存是连续的,按行存储;图
二维数组的初始化:
1.{}给部分元素/全部元素赋值时,可以省略行大小不写
给部分元素赋值时:行大小以最接近元素个数的列数的倍数填充
2.{ {},{},{} };
元素的访问:数组名[行下标][列下标];
第三行第二列:a[2][1]
字符数组:
char ch[3][6]={“abcdd”,”efgh”,”ijkl” }

1.字符串拷贝
1.将src中的元素一一赋值给dest的元素,直到’\0’

1.for (int i = 0; (dest[i] = src[i]) != '\0'; i++);
2. int i = 0;
	  while ((dest[i] = src[i]) != '\0') 
		i++;

2.库函数实现:1.目的字符串的数组名 2.源字符串的数组名
strcpy(dest, src);//字符串拷贝

2.字符串连接
1.方法实现:
//1.找到要连接的位置
int i = 0;//i表示’\0’的下标
while (dest[i] != ‘\0’)
i++;
//2.连接
for (int j = 0;(dest[i + j] = src[j]) != ‘\0’; j++);
2库函数实现:
1.被连接的字符数组名 2.连接的字符数组名
strcat(dest,src);//字符串连接

3.字符串比较 ························
1.方法实现:

int value = 0;//表示两个字符串相等
	int i;
	for (i = 0; dest[i] != '\0' && src[i] != '\0'&&dest[i] == src[i]; i++);
	///1.不相等 2.最后一个
	if (dest[i] < src[i])
		value = -1;
	else if (dest[i] > src[i])
		value = 1;
	cout << value << endl;
2. 库函数实现:
strcmp("ab\0", "ab");

4.子串在母串中出现的次数

char motStr[30] = "aaababaa";
	char subStr[5] = "aa";
	//1.遍历母串
	int count = 0;
	for (int i = 0; motStr[i] != '\0';i++)
	{
		//2.遍历子串
		for (int j = 0; motStr[i + j] == subStr[j]; j++)
		{
			//判断子串对应的j+1下标如果为'\0',则表示已经找到
			if ('\0' == subStr[j + 1])
			{
				count++;
				break;
			}
		}
	}
	cout << count << endl;
	return 0;
}

杨辉三角:

int main()
{
	//杨辉三角
	int n[SIZE][SIZE];//SIZE:宏变量
	for (int i = 0; i < SIZE;i++)
	{
		for (int j = 0;j <= i;j++)
		{
			//左右两边数都为1
			if (0 == j || j == i)
			{
				n[i][j] = 1;
			}
			else
			{
				n[i][j] = n[i - 1][j - 1] + n[i - 1][j];
			}
		}
	}
//打印输出
	for (int i = 0; i < SIZE; i++)
	{
		for (int j = 0; j <= i; j++)
		{
			cout << setw(3) << n[i][j];// setw(3)控制每个数字的宽度
		}
		cout << endl;
	}

六、 函 数:
概念:对特定的功能进行封装
定义:由返回值,函数名,参数表和函数体组成。
返回值类型 函数名(形式参数表)
{
函数体
}
输出函数输出字符串“hello game”
void 空类型
1.确定返回值类型
2.确定函数名
3.确定参数表,参数表可以省略,但是括号不能省
4.参数表之中的参数用逗号隔开

调用:函数名(实参表)
注意:C++函数体内不能再定义其他函数,函数是并行的。
不能嵌套定义,可以嵌套调用
函数调用时:实参表和形参表类型,数量一一对应。
实参可以是常量,变量,表达式

  1. 作为函数语句
    Printf(“hello\n”)
  2. 函数表达式
    Int num=sum(a+3,3);
  3. 作为其他函数的实参;顺序:从内往外调用;
    outputInt(sum(10, 9));
  4. 函数的调用流程:
    (调用的地方:主调函数 被调用的函数:被调函数)
  5. 从主调函数找到被调函数
  6. 系统给形参分配临时内存,将实参的值拷贝(赋值)给形参
  7. 执行函数体,有返回值将返回值带回主调函数
  8. 系统回收形参的临时内存
  9. 回到主调函数

声明:返回值类型 函数名(参数表);
写在调用之前就可以
形参和实参的区别:
1、 形参出现在函数定义时,实参出现在函数调用时。
2、 形参只在函数调用之后被分配内存,调用完释放形参内存。
3、形参的作用域只在函数内,出被调函数释放,实参进入被调函数后无法使用
七、 指 针:
1.概念:为了方便访问内存中的内容,给每一个内存单元编号,把这个编号称为地址,也就是指针。
2.定义:(也是一种数据类型)
类型 指针名
(指针指向的类型 * )指针名;(:标注这个变量为指针)
//定义了一个int
类型的变量,变量名叫p
//p指向int类型的数据(p表示int类型数据的地址)
int* p;
//定义了一个int类型的变量,变量名叫p1
//p1指向int类型的数据(p1表示int类型数据的地址)
//该数据指向int类型的数据
int
p1;
//定义了一个大小为5的数组,数组元素都为short类型
//数组名为p2
//
short
p2[5];
3.指针的内存:
所有的指针都占四个字节;
4.指针的赋值:
&:取地址符:&(&变量)
解析引用符:只能跟指针(表示取指针指向的内容)
1.用指向类型的数据的地址赋值
int hp = 100;
//定义了一个int
类型的变量p-
//p指向int类型的数据hp(p表示int类型数据的地址)
int
p=&hp;
int* p1=p;

   2.用相同类型的指针赋值
      int* p1=p;
   3.直接用地址赋值
      int* p4 = (int*)0x12abcd89;
   4.用字符串给字符指针赋值
      char* p = "abcd";
   5.用数组名赋值

int n[5];
int* p4 = n;
6.指针没有明确指向时,赋值为空
&,可以抵消,&不能抵消.
解析引用符:取内容字节数 = 指针指向的数据的字节数
short
:short int
:

使用cout输出char型时,
从这个地址开始直到\0的字符串全部输出.
指针练习及解析:
一、
int a = 3;//定义了一个名为a的int型变量,并给它赋初值为3
int
p = &a;//定义了一个int*型的指针p,p指向a

*p = 4;//把整数4赋值给p指向的内容(实际上为a),p的指向的a变为4
cout << "a=" <<a << endl;//输出a=4
(*p)++;//*p指p指向的a,所以(*p)++相当于a++,因此a=4++=5
int b = 4;//定义了一个名为b的int型变量,并给它赋初值为4
int c = b**p;//定义了一个名为c的int型变量,并给它赋初值为b**p;
//b**p=b*(*p),*p指p指向的a,而a=5,所以b**p=b*5=4*5=20;
cout << "*p=" << *p << endl;//输出*p=5;*p指p指向的a,即a=5;
cout << "a=" << a << endl;//输出a,a=5;
cout << b << c << endl;//输出b,b=4;输出c,c=20;

int** p1 = &p;//定义了一个名为p1的int**型变量,p1指向p;
**p1 = c++;/*把c的值赋给**p1指向的内容,即*p指向的内容,即a,所以a=c=20;然后c++ c=21;*/
*p1 = &b;//把b的地址赋给*p1指向的内容,即p指向b;
(**p1)++;//**p1指向的内容自增,即b自增,即b=5;
p = &c;//把c的地址赋给指针p,p指向c;
*p = a***p1;//把a***p1赋值给*p指向的内容,即c,
//a***p1=a**(*p1)=a**p=a*(*p)=a*c=20*21=420;
cout << a << b << c << endl;//输出a,b,c的值,分别为:20 5 420
cout << *p1 << endl;//输出C的地址;p=&c
cout << &c << endl;//验证*p1是否是c的地址
cout << *p << endl;//输出*p指向的内容,即c,所以输出420

二、
char ch[5] = “abcd”; //定义了一个大小为5的char型数组ch,并给它的元素赋值为abcd;
char* p = &ch[1];//定义了一个char型的指针p,p指向ch[1];
charp1 = &p;//定义了一个char型的指针p1,p1指向p;
cout << p1 << endl;//输出p1指向的内容,即p,p为char
型,所以输出:bcd
ch = (p1)++;//把p1指向的内容赋值给ch指向的内容,然后**p1指向的内容自增
//*ch指向ch[0]:a;**p1指向ch[1],所以,ch[0]=ch[1]=b;ch[1]++=c;
*p1 = &ch[3];*p1指向p,p = &ch[3];p指向ch[3]
cout << *p + 3 << endl;//*p指向ch[3],所以输出‘d’+3=100+3=103
*p1 = &ch[2];//*p1指向p,p = &ch[2];p指向ch[2]
cout << *p1 << endl;//p1指向p,p为char型,所以输出cd
(*p)++;//p指向的内容为ch[2];所以ch[2]=c++=d;
cout << p << endl;//输出p指向的内容,即ch[2],即d
cout << ch[2] << endl;//输出ch[2],即d
cout << ch[2] + 1 << endl;//输出ch[2]+1,即d+1,提升为整型,所以输出100+1=101;
cout << p << endl;//应为p为char
型,并且p指向ch[2],所以输出dd

三、
char ch[5] = “ijkl”;//定义了一个大小为5的char型数组ch,并给它的元素赋值为ijkl;
char* chArray[3] = { ch, &ch[1], &ch[3] };//定义了一个大小为2的char型数组chArray,
//并且它的元素分别指向ch, &ch[1], &ch[3];
char** pArray = chArray;//定义了一个char**型变量pArray ,pArray指向chArray数组的首元素;
int i = 1;//定义了一个int型变量i,并给它赋值为1;
ch[i++] = (**pArray) + 3;//把(**pArray) + 3赋值给ch[i++],ch[i++]即为ch[1],
//,**pArray指向的内容为ch[0];ch[1]=ch[0]+3=l;并且i++,i=2
pArray = &chArray[2];//把chArray[2]的地址赋值给pArray,pArray指向chArray[2];
cout << pArray << endl;//输出pArray指向的内容,即&ch[3],为char
型,所以输出l
cout << &ch[i] << endl;//输出&ch[i],i=2;所以输出&ch[2],又因为&ch[2]为char型,所以输出kl
pArray = &ch[0];//把ch[0]的地址赋给pArray指向的内容,即chArray[2]指向ch[0];
(**pArray)–;//指向的内容为ch[0],所以ch[0]–,ch[0]=h;
cout << pArray << endl;//输出pArray内容,即ch[0],所以输出h
pArray = &chArray[1];//把chArray[1]的地址赋给pArray,pArray指向chArray[1];
cout << **pArray << endl;//**pArray 指向的内容为chArray[1],所以输出chArray[1]
//chArray[1]为char
型,所以输出jkl
cout << ch << endl;//ch为char*型,所以输出hjkl
cout << **pArray + 5 << endl;//*pArray指向ch[1],所以输出’j’+5,提升为整型;所以等于113
cout << (pArray)[1] << endl;//pArray所指内容向右偏移1,所以输出k
5.指针的运算:
1.加减法:指针加上或者减去一个整数n,表示指针偏移n个单位.
(单位 = 指针指向的类型所占字节)
int
p;
p+1;//p+1偏移一个单位 = 4个字节
short
p;
p-1;//p-1 = 向左4个字节
2.指针 - 指针:相差的单位数
3.指针的自增自减:
p++;
p–;指针本身偏移1个单位;
4.指针[]:p[1],p[-1];
以p为首地址的第n+1个元素;
p[n] = *(p+n);

数组名和指针除了自增自减和赋值外,无区别.

大端存储(与阅读习惯一致):数据的高位存在内存的低地址,
数据的低位存在内存的高地址.(读取数据:低地址 - 高地址)
小端存储(win32)(与逻辑习惯一致):数据的高位存在内存的高地址,
数据的低位存在内存的低地址.(读取数据:高地址 - 低地址)

nullptr:强指针(只能赋值给指针)
6.数组指针:

数组指针:指向数组的指针.
1.数组指针的定义:
元素类型(*数组指针名)[列数n];指针指向列数为n的一维数组
数组指针的赋值:
1.一维数组名的地址
2.二维数组名

2.数组指针的偏移:
p+1:偏移一行(1个单位 = 每个元素所占字节 * 列数)
p++:
p[n] = (p+n)
指针数组:数组的元素为指针.
char
n[5];
含义上,n是&n[0]
值:n = n[0] = &n[0][0]
/cout << n << endl;
cout << n[0] << endl;
cout << &n[0][0] << endl;
/

定义了一个数组指针p,p指向列数为4,
元素类型为int型的一维数组.
n是{n[0],n[1],n[2]}的数组名,n等于&n[0]
n[0]是{n[0][0],n[0][1],n[0][2],n[0][3]}的数组名
n[0]是&n[0][0]
int(*p)[4] = n;//n等于&n[0]

定义了一个数组指针p1,p1指向列数为5,元素类型为char型的一维数组
char
(*p1)[5];
1.数组:数组大小与数组名不用括号分开
2.指针:指向的类型 * 指针名
定义了一个指针p2,指针指向数组指针
该数组指针指向列数为4,元素类型为int类型的一维数组
int(**p2)[4] = &p;
数组:元素类型 数组名[数组大小]
定义了一个大小为5的数组p3,
元素类型为数组指针类型,该数组指针
指向列数为4,元素类型为int型的一维数组
int(*p3[5])[4] = {p,p,p,p,p};

含义:n[0]是第一行的数组名,是&n[0][0],为int*型
cout << n[0] << endl;
cout << &n[0][0] << endl;
int* p = &n[0][0];

7.多维数组:
数据类型 数组名[维数1][ 维数2][]……[ 维数n];
int n[3][4][5];
指向一维数组及以上的指针都是数组指针.
元素类型 (指针名)[维数2]…[维数n]
typedef:给类型取别名。(写在函数外,一般写在函数前)
typedef 类型 别名;
观察什么类型可以将typedef去掉
void
:空指针类型(不确定类型的指针)
1.可以指向任意类型的数据
2.因为没有明确的指向,所以不能偏移和取内容。

指针与const的结合:
(非const可以给const赋值,const不能直接给非const赋值)
1.常量指针:表示指针指向一个常量
(const 指向类型)指针名;const int n;
(指向类型 const)指针名;int const n;
常量指针不能改变指向的内容,但是可以改变指向。
2.指针常量:指针本身被const修饰的变量,是只读的
指向类型* const 指针名
1.必须初始化
2.指针常量不能改变指向,但是可以改变指向的内容
3.常量指针常量:
1.必须初始化
2.既不能改变指向,也不能改变指向的内容

区分常量指针和指针常量:
const在之前是常量指针,在后就是指针常量

8.内存区域的划分:
1.常量区(不能被修改):
常量(1,100,1.1,‘a’,“abcd”),字符串
程序开始时系统分配内存,程序结束时系统自动回收内存.
2.全局区(静态存储区):
全局变量(定义在函数外的变量)和
静态(static)变量(static 类型 变量名;)
1.只初始化一次,默认为0
2.内存只有一份
程序开始时系统分配内存,程序结束时系统自动回收内存.

3.栈区:局部变量(定义在函数内的变量)
进入函数时系统分配内存,函数结束时系统回收内存.

4.堆区:由程序员手动申请,手动释放.(申请-释放-置空)
C语言(函数):
1.形参:申请的字节数 2.返回值:申请内存的首地址
void *malloc( size_t size );
函数返回一个指向num 数组空间,
每一数组元素的大小为size。
如果错误发生返回NULL。
void *calloc( size_t num, size_t size );
释放内存:free(内存首地址);

C++(运算符):
int* p = new int;//返回申请内存的首地址
int* p = new int(元素初始化);//给这段内存存入值
int* p = new int[数组大小];//申请连续的内存

delete 内存的首地址;//释放1个指向大小的内存
delete[] 内存的首地址;//释放一段连续的内存
申请:

int** pMap = new int*[h];//申请内存(动态二维数组)
for (int i = 0; i < h; i++)
{
pMap[i] = new int[w];
}

释放:

for (int i = 0; i < h; i++)//释放内存
		  {
			 delete[] pMap[i];
			 pMap[i] = NULL;
		  }
		 delete[] pMap;
		 pMap = NULL;

全局变量和全局static变量区别:
1.生存周期相同,从程序开始分配内存,程序结束回收内存.
2.作用域不同,全局变量可以作用于所有文件,需要extern声明
全局static变量只能作用于当前所在文件.

局部变量和局部static变量区别:
1.生存周期不同,局部变量进入函数时系统分配内存,函数结束时系统回收内存.
局部static变量程序开始时系统分配内存,程序结束时系统自动回收内存.
2.作用域相同.(函数内)

9.指针补充
1.内存泄漏:内存没有释放,指针改变了指向
2.野指针(坏指针):释放了内存,但指针没有置空

使用指针:
1.指针必须有指向(没有赋值和指向NULL为两个概念)
2.数组越界

引用(实质上是指针)的定义:给变量取别名.
&:引用符
类型& 变量名;例如:int& a;
1.必须初始化
2.不能改变引用的对象
常引用(不能改变被引用对象的值):引用的是一个常量.
const 类型& 变量名;

指针和引用的区别:
1.引用必须初始化,指针可以不初始化.
2.引用不能赋值为空,指针可以赋值为空.
3.引用与被引用对象共享一段内存,指针有独立的内存.
4.引用不能改变引用的对象,指针可以改变指向.

动态一维数组:手动申请了5个int类型大小的内存,内存连续
p指向内存的第一个元素

int* p = new int[5];
	delete[] p;
	p = NULL;
	cout << *p << endl;
	p = new int[2];
	delete[] p;
	p = NULL;

动态申请二维数组内存(行与行之间内存不一定连续)
申请3行5列
1.申请3个int*类型大小的内存,
保存之后的每一行的首地址

int** p1 = new int*[3];
	for (int i = 0; i < 3; i++)
	{
		p1[i] = new int[5];
	}
	for (int i = 0; i < 3; i++)
	{
		delete[] p1[i];
		p1[i] = NULL;
	}
	delete[] p1;
	p1 = NULL;

八、头文件:
1.文件管理:
头文件-右键-添加新建项-.h文件
2.引用自定义文件:用""引用
引用头文件的意义:整个头文件替换到引用处
3.项目文件夹分类
引用带路径的头文件:/
引用根目录上一层的路径:…
4.筛选器:方便在解决方案面板管理
(保持与外部文件夹同步)
注意:
1.将函数的声明写在.h文件中,将函数的定义写在.cpp文件中
(函数的声明可以多次,函数的定义只能一次)
(最好.h和.cpp文件同名)
2.全局变量的声明也放在.h文件中,定义写在.cpp文件中
(extern时给变量赋值变为定义,可能报重定义错误)
3.宏定义可以写在头文件中,定义多次不会报错,
后面的定义覆盖前面的定义.

1.头文件不进行编译,源文件分别编译
2.无法解析的外部符号:
函数:只写了声明没有写定义
无法解析的外部符号
变量:只写了声明没有写定义

九、函数补充:
函数的形参:
1.形参为数组时,会被弱化成指针
一维数组:
1.int n[10],10不代表数组的大小.
2.int n[];
3.int n[],int size;//size表示数组的大小
4.int* n;
5.int* n,int size;//推荐
2.二维数组:被弱化成数组指针
1.int n[3][10];
2.int n[][10];//不能省略列大小
3.int n[][10],int row;//row指行大小
4.int(*p)[10];//
5.int(*p)[10],int row;//推荐,加上行大小

3.形参为被const修饰时:
1.修饰普通类型,防止形参被修改
2.修饰指针时,常量指针(防止指向内容被改变)
3.修饰引用时,常引用(防止被引用对象的值被改变)

返回值:不能返回栈区的地址或者引用.
1.返回值为指针:
2.返回值为引用:既可以作为左值,又可以作为右值.
作为左值时:被引用对象被赋值.
作为右值时:1.给普通类型赋值:将值赋值给变量
2.给引用类型赋值时:该引用类型也引用该变量
特殊情况:返回常引用(防止通过返回值修改被引用对象的值)

函数:
优点:可以重复使用,节省空间,使结构更清晰.
缺点:查找函数,时间上的消耗.

短小(没有循环)且频繁使用的函数:
1.C语言:宏定义函数(宏替换)
#define 宏定义名(形参表:不需要类型) 函数体;
1.(宏替换),整体思维,注意()
2.不进行类型检测
3.传递类型
4.宏定义函数有多行时,每一行末尾用\结束
(\紧跟换行符)

2.C++:内联函数(函数体替换到函数调用处)
以空间换取时间的函数.
在函数定义前面加上inline关键字.
4.函数重载:
函数名相同,形参不同.(类型/顺序/个数其中至少一者不同)
1.参数传递时,只能低精度往高精度转化,否则进行强转
2.返回值类型不同,不能称为重载.
报错:无法重载返回类型区分的函数.
3.仅参数名不同,不能称为重载.
函数在C++内部编译:函数名_形参类型1_形参类型2
sum_int_int
C语言不支持重载.

5.函数缺省(参数缺省):形参有默认的实参值.
形参类型 变量名 = 默认值;
缺省顺序:从右往左;
参数缺省写在函数声明中;
int sum(int a = 1,int b = 2);//正确
int sum(int a,int b = 2);//正确
int sum(int a = 1,int b);//错误
1.不传递缺省实参,以默认值为准
2.传递实参,实际实参覆盖默认实参

6.函数指针:指针指向一个函数.(函数占内存)
函数指针(是一种数据类型)的定义:
返回值类型 (*函数指针名)(形参表(不需要变量名));
//pfunc指向返回值类型为int类型,形参为int,int的函数.
int(*pfunc)(int,int);
指针函数:返回值为指针的函数.

赋值:
1.&函数名(都表示指向函数)
2.函数名(推荐)

使用函数指针调用函数:
函数名(实参表);
1.(*函数指针)(实参表);
2.函数指针(实参表);(推荐)

7.const的至少三种用法:
 指针常量:int* const p;//必须初始化
 常量指针:const int* p;
 常量指针常量:const int* const p;
 常引用:const int& a = b;
 const修饰普通类型的变量:表示只读
8.递归
1.斐波那契数列:1 1 2 3 5 8 13 21…
求第n项.
fab(n) = fab(n-2)+fab(n-1)
2.求!n:n的阶乘
3.猴子吃桃:第一天猴子吃了桃子总数的一半加1个,
第二天吃了剩下桃子数的一半加1个,按照这种吃法,
第10天时,发现只剩一个桃子,请问第一天有多少桃子?
第二天 = 第一天/2 - 1
第一天 = (第二天 + 1)*2
peach(day) = (peach(day+1) + 1) * 2;
递归:函数直接或者间接的调用自身.
递推:函数调用的过程.
回推:返回的过程.
当递归跳不出来时,栈溢出(stack overflow)
1.找规律 fab(n) = fab(n-2)+fab(n-1)
2.找跳出条件

if (1 == n || 2 == n)
return 1;
//mul(n) = n*mul(n - 1);
int peach(int day)
{
	if (10 == day)
		return 1;
	return (peach(day + 1) + 1) * 2;
}
int mul(int n)
{
	跳出条件
	if (1 == n)
		return 1;
	return n * mul(n - 1);
}
int fab(int n)
{
	//跳出条件
	if (1 == n || 2 == n)
		return 1;
	//规律
	return fab(n - 1) + fab(n - 2);
}

int fab(int n)
{
	//如果n为1和2,返回1
	if (1 == n || 2 == n)
		return 1;
	//第n项等于前两项之和
	int a = 1, b = 1;//a表示n的前两项,b表示n的前一项
	for (int i = 3; i <= n;i++)
	{
		int t = b;	
		b = a + b;
		a = t;
	}
	return b;
}

函数:1.不能出现功能重复的代码
2.不要通过修改数据

项目三部曲:
1.初始化:数据的准备(执行一次)
2.更新:数据的改变(循环)
3.绘制:最终效果的显示(循环)

//防止头文件被重复引用
#ifndef _HERO_ //--如果没有定义
#define _HERO_ //--定义
*****
#endif

十、自定义类型:
结构体/联合/枚举(由基本数据类型组成)

  1. 结构体:
    概念:一类具有相同属性和行为的事物的封装
    英雄:属性:血量/等级/蓝/经验…
    行为:移动/攻击…
    骑车:属性:颜色/型号/速度…
    行为:移动/加速/停止…
    1.结构体的定义:
    //struct:关键字,表示是一个结构体

struct 结构体类型名//类型名首字母大写
{
数据成员1;//数据类型+变量名
数据成员2;
数据成员n;

}
2.内存:结构体对齐
以所有类型中最大内存为单位分配字节.
4+4+4+4(hp)+4(lv)=20
定义结构体时,内存从小到大定义成员
3.结构体变量的定义:
结构体类型名+变量名;
4.初始化:{}//按照成员的顺序一一赋值,中间用逗号隔开
5.赋值:
1.{}
2.单独给成员赋值:
成员访问:成员选择符(.)
6.结构体类型指针的定义
结构体类型 * 指针名
Hreo* p=&hero;//p指向hero
通过结构体类型指针访问成员:1.(*p).lv
2.成员选择符(->):指针->成员名
7.结构体数组:
结构体类型 数组名[数组大小];

  1. 联合类型
    定义:

    union 联合类型名
    {
    联合成员1;//数据类型+变量名
    联合成员2;
    };

内存:内存对齐(所有成员共享一段内存)

  1. 枚举类型:(表示状态值.)
    方向的状态:上下左右
    游戏状态:开始/暂停/游戏中/结束
    物品类型:药品/装备
    定义:

    enum 枚举类型
    {
    状态值1,//UP(推荐)/up
    状态值2,

    状态值n
    };

内存:枚举变量为一个int类型的大小,
每一个枚举状态值都对应一个整数,
如果没有给枚举状态赋值,从0开始,依次+1.
如果给其中某个枚举状态赋值,从此之后的所有状态:基础上+1.
例如:

enum Dir
{
	UP,
	DOWN,
	LEFT,
	RIGHT
};
  1. 读取/存储文件:
    1.存储文件:

    void saveFile(int** pMap, int row, int line, int level)
    {
    //1.文件路径 2.模式写入wb+ 读取rb+
    //printf(“map_%d.txt”, level);
    char fileName[20];
    sprintf(fileName, “Map/map_%d.txt”, level);//printf输出到控制台,sprintf输出到字符数组中
    FILE* pfile = fopen(fileName, “wb+”);//打开1个文件,如果没有,则自动创建
    //写入:1.被写入的数据的首地址 2.1个数据的大小
    //3.数据的个数 4.FILE*
    for (int i = 0; i < row; i++)
    {
    //fwrite会偏移count个size大小的内存
    fwrite(pMap[i], sizeof(int), line, pfile);
    }
    //关闭文件
    fclose(pfile);
    }

2.打开(读取)文件:

void readFile(int(*pMap)[10], int row, int line, int level)
{
	char fileName[20];
	sprintf(fileName, "Map/map_%d.txt", level);
	//1.打开文件
	FILE* pfile = fopen(fileName, "rb+");
	fread(pMap, sizeof(int), row * line, pfile);
	fclose(pfile);
}
//exit(0);//退出程序

十一、类:

  1. C语言:面向过程,以函数为核心.
  2. C++语言:面向对象,以类为核心.

1.类的概念: 一类具有相同属性和行为的事物的封装.
2.类的特性: 封装/继承/多态
//声明和定义各一个文件(.h和.cpp同名)
3.类的定义:

class 类名
{
	成员1;//数据成员和成员函数
	成员2;
};

class Hero //Hero是一种数据类型
{
public://:冒号
	 Hero();//默认普通构造函数
	 Hero(int hp);//带参构造函数
	 Hero(const Hero& other);//拷贝构造函数
private:
	int lv;
	int exp;
public:
	int hp;
	void pk();
	void die();
};

4.struct和class的区别:
1.struct是C语言的,而class是C++的.
在C++中struct和class都能用,
在C语言中只能用struct.
2.成员访问权限:
struct默认为public,
class默认为private.
访问权限:
public:公有的,成员可以在类中和类外访问.
protected:保护的,成员可以在类中和子类中访问.
private:私有的,成员只能在类中访问.(默认)
1.三种访问权限不一定全部写.
2.数据成员一般指定为私有的.
成员函数一般指定为公有的.
3.重复访问权限不报错

5.类的对象(变量)创建:
1.类名 对象名;Hero hero;
2.new 类名;new Hero;

普通全局函数和类的成员函数的区别:
类的成员函数必须通过对象调用.

每一个成员函数中:this指针(可省略不写)
this指向当前调用函数的对象
6.类的四大默认成员函数:
1.普通构造函数(默认:没有形参)
1.没有返回值类型
2.函数名与类名完全相同
3.普通构造函数可以重载(普通构造函数可以有多个)
调用:给对象分配内存时调用.
(1.类名 对象名; 2.new 类名;)
调用无参:1.类名 对象名; 2.new 类名; 3.new 类名();
调用带参:1.类名 对象名(实参表);
2.new 类名(实参表);
//默认普通构造函数(无参构造函数)的定义

Hero::Hero()
{
	cout << "调用了无参构造函数" << endl;
}		
Hero::Hero(int hp)
{
	cout << "调用了带参构造函数" << endl;
}

2.拷贝构造函数(复制构造函数)
1.没有返回值类型
2.函数名与类名相同
3.形参为const 类名& 对象名;
调用:1.用类的对象给另一个对象初始化时;
Hero hero1 = hero;//定义时赋值
2.显示调用拷贝构造函数;
Hero hero(hero1);
const Hero& other = hero1;
3.形参为类的对象时:将实参的值拷贝给形参
void test(Hero hero);
4.返回值为类的对象时
Hero::Hero(const Hero& other)
{
cout << “调用了拷贝构造函数” << endl;
}

当任意构造函数被显示给出时,
不能调用系统默认的无参构造函数,
也需要显示给出.

3.赋值函数:
类名& operator=(const 类名& other);
A& operator=(const A& other);
调用:用一个类的对象给另一个对象赋值时;
Hero hero;hero = hero1;
将hero1的成员的值一一拷贝给hero.
4.析构函数:
1.没有返回值类型
2.函数名:类名(A ~Hero)
3.没有形参
4.不支持重载
调用:内存被回收或者释放时.
对于系统回收的内存:先构造的后析构.
对于手动申请的内存:根据delete先后顺序.
5.类中数据成员为一个类的对象时;
6.没有内存泄漏的情况下,构造函数和析构函数的个数相同.
7.malloc/free和calloc/free不会调用构造函数和析构函数.
8.类的对象的内存:
1.内存对齐
2.空类:1个字节.
十二、类的封装
1.A类中具有B类的子对象,
构造函数:B-A
析构函数:A-B
2.默认拷贝构造函数:浅拷贝(将一个对象的值
一一拷贝给另一个对象)
浅拷贝可能会造成两个指针指向同一段内存.
当类的成员中含有指针并且指向堆区内存时,必须重写拷贝构造函数,
实现深拷贝.

Map::Map(const Map& other)
{
	//m_row = other.m_row;
	//m_line = other.m_line;
	//pMap = other.pMap;
	getMemory(other.m_row, other.m_line);
	//将other.pMap指向内存中的值拷贝给this->pMap
	for (int i = 0; i < m_row;i++)
	{
		memcpy(pMap[i], other.pMap[i], sizeof(int)*m_line);
	}	
}

3.数据成员赋值:
1.访问权限为public:对象.成员 = 值;
2.在类外访问私有成员,公有的成员方法:get/set函数
3.构造函数
4.成员初始化列表:(在分配内存时初始化)

Hero::Hero(int h, int l)
	:hp(h), 
	 lv(l)
{
	/*hp = 100;
	lv = 2;*/
	getLv();
}

4.成员函数:
数据成员和成员函数:
const:增加程序的稳定性.
const数据成员:1.必须使用成员初始化列表初始化
2.不能修改该成员的值
math:PI
const的成员函数:
返回值类型 函数名(形参表) const;
1.不能在函数内修改成员的值.
2.形参的值可以修改.
3.函数内不能调用非const的成员函数.

5.static数据成员:
1.必须在类外初始化
类型 类名::变量名 = 值;//如果没有赋值,则默认为0
2.内存在全局区,程序结束内存才回收,
内存不属于类的一部分.
3.被类的所有对象共享,一个对象改变了其值,
其他对象访问时也是改变后的值.
4.1.静态成员可以通过类的对象调用
2.直接通过类名调用:A::a;

6.static成员函数:(在函数返回值前加上static)
函数内部只能访问static数据成员或者调用static成员函数.
管理者:只有1个.
设计模式:23种(大话设计模式)
单实例模式:只能创建一个对象.(程序结束才回收内存)
1.构造函数私有化
2.在类中写一个函数(静态)用来创建对象
3.保存第一次创建的实例的地址
(static成员函数只能访问static成员)
1.在类外不能创建对象:构造函数私有化.
2.在堆区创建对象:析构函数私有化
7.友元:实现数据共享(可以访问私有成员)
破坏了类的封装性.
1.友元函数:(相当于全局函数)
1.不属于类的成员函数,没有this指针.

2.声明:类中
friend 返回值类型 函数名(形参表);
3.定义:类中和类外都可以
不需要加类的作用域符号.
4.调用:函数名(实参表);不需要通过对象调用

2.友元类:
1.friend 类名;
1.不可逆性:A是B的友元类,B不一定是A的友元类.
2.不可传递性:A是B的友元类,B是C的友元类,A不一定是C的友元类.
3.不可继承性:A是B的友元类,C是A的子类,C不一定是B的友元类.

8.运算符重载:实现两个自定义类型对象之间的运算.
1.类的成员函数:
返回值类型 operator运算符(形参表);
函数名:operator运算符
形参表个数:运算符左右的式子个数-1
2.友元函数:
形参个数为式子左右个数.
一切能写成成员函数的重载都可以写成友元,只是形参个数+1.
能写成友元函数的重载不一定能写成成员函数,
比如运算符的左边的式子不为该类的对象.(必须使用友元)
cout<<hero;//运算符:<< 左边:ostream 右边;Hero
cout.operator<<(hero);
//hero<<cout;//hero.operator<<(cout);

9.指向类的数据成员
定义:成员的类型 类名::*指针名 = &类名::成员名;
在类外指针指向:只能指向公有成员
int Hero::*p = &Hero::hp;//p指针指向成员hp
Hero hero;
hero.hp = 100;
通过指针访问成员

hero.*p = 200;
			cout << hero.hp << endl;
			Hero hero1;
			hero1.*p = 300;
		    cout << hero.hp << endl;
			cout << hero1.hp << endl;
			Hero* pHero = new Hero;
			pHero->hp = 500;
			pHero->*p = 600;

10.成员函数指针
返回值类型(类名::*成员函数指针名)();
赋值:1.&类名::成员函数名
调用:(对象.*成员函数指针名)(实参表);
(指向对象的指针->*成员函数指针名)(实参表);
指针指向Hero的getHp成员函数
int(Hero::*pFunc)() = &Hero::getHp;
cout<<(hero.*pFunc)()<<endl;//等同于hero.getHp();
(pHero->*pFunc)();//pHero->getHp();

十三、类的继承
1.继承:除了具有父类的属性和行为外,
还具有自己特定的属性和行为.
父类:桌子 子类:圆桌/方桌等
父类:动物 子类:猫/狗等
父类:物体 子类:英雄/怪物等
子类也是父类中的一种.
父类派生子类/子类继承父类.
定义:产生了新类.
class 类名:派生权限 父类名1,派生权限 父类名2…
{
访问权限:
//独有的数据成员和成员函数
};
成员访问权限
派生权限: public
protected
private

public
public
protected
不可访问

protected
protected
Protected 不可访问
private
private
private
不可访问

构造函数:父类构造函数(按照继承的顺序从左往右)-子类构造函数
析构函数:子类析构函数-父类析构函数(按照继承的顺序从右往左)
调用父类的带参构造函数:
成员初始化列表
(防止访问非法内存):
父类与子类对象之间的赋值:
子类对象可以给父类对象赋值,
父类对象不可以给子类对象赋值.

子类指针可以给父类指针赋值.
父类指针不可以给子类对象赋值.
内存:内存对齐,为父类成员+子类成员的大小.
String类的四大函数

十四、类的多态:
1.单实例模式/外观模式(Game)/简单工厂模式
2.隐藏:当子类中出现与父类同名的成员(二义性),
父类成员被隐藏.
同名方法不同形参时,子类对象也不能调用父类方法.
3.基类指针指向子类对象时,如果成员方法前
没有virtual关键字,则调用为基类函数.
4.指针调用成员或者非virtual成员函数时,
根据指针指向的类型.(静态多态(编译期多态))

多态:对同一消息的不同响应.
5.动态多态(运行时多态):继承+虚函数
(在函数声明前加virtual关键字)
静态多态:看指针类型
动态多态:看内存
6.具有虚函数的类的大小:+4个字节(虚指针内存)
虚表:按顺序存放了该类的所有虚函数的地址.
虚指针指向虚表.
虚函数:对象.虚函数(实参表);//通过虚指针找虚表中的虚函数
7.虚函数也可以被继承,
1.如果没有重写(override)虚函数,则父类与子类对象
虚指针指向的虚表中的内容相同.
2.如果重写:
1.virtual关键字可写可不写
2.函数形参表后override可写可不写,
写了表示重写父类的某函数
(如果父类没有该函数则会报错)

  1. 虚析构函数:基类指针指向子类对象,用基类指针释放对象,
    如果基类不使用虚析构函数,不会钓鱼派生类析构函数,
    需要在子类析构函数内释放内存时.
    2.在子类中产生一个新的虚函数,
    不会产生新的虚指针.而是原先的虚指针
    指向虚表,虚表添加一个函数的地址.
    3.哪些函数不能定义为虚函数(虚指针+this)?
    1.构造函数(普通/带参/拷贝):没有为虚指针分配内存
    2.静态函数:可以直接通过类名调用(也没有虚指针内存)
    3.友元函数:不属于类的成员,没有this指针.
    4.普通非成员函数:不属于类的成员.
    5.内联函数:在编译阶段展开,虚函数是运行时多态.
    6.纯虚函数:virtual 返回值类型 函数名(形参表) = 0;

抽象类:只有声明,没有定义(函数体)具有一个或以上纯虚函数的类称为抽象类.
Animal:
Object:
1.不能创建对象,可以创建指针或者引用.
2.抽象类的子类必须实现纯虚方法,才能
创建对象.
8.转换:
1.强转(不安全性)
2.四种安全类型转换
转换类型关键字<转换类型>(转换内容);
const_cast:
1.消除或者添加const属性
2.指针或者引用操作
static_cast:静态转换
1.普通类型之间的转换
2.指针间的转换(父子级关系,考虑偏移)
reinterpret_cast:重新解释转换
1.void到任意指针之间的转换
2.任意指针到void
的转换
3.任意指针到任意指针之间的转换
dynamic_cast:动态转换
1.继承+虚函数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值