C++基础知识

C++基础编程

C++入门

模板

# include <iostream>
using namespace std;

int main()
{
  cout<<"hello world"<<endl
  system ("pause")
  return 0   
}

注释

单行注释 // 描述信息

多行注释 /* 描述信息

变量

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

常量

不可更改的数据,定义方式:

#define 宏常量,一般写在main外面

#define Day 7
int main(){
  cout<<"一周总共有:"<<Day<<endl
  system("pause")
  return 0
}

const修饰的变量

一般写在main里面

int main(){
  const int month = 12 // const修饰的变量也是常量
  cout<<"一年总共有:"<< month <<endl 
  system("pause")
  return 0
}

关键字

比如int float long等等

标识符命名规则

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

标识符中字母区分大小写

C++常见数据类型

数据类型存在的意义:给变量分配合适的内存空间

  • 整型

short(2字节)-215~215-1

int(4字节)-231~231-1

long(4字节)

long long(8字节)-263~263-1

sizeof关键字

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

  • 实型(浮点型)

float(4字节)7位有效数字

//默认输出一个小数,会显示出6位有效数字

int main(){
  float f1 = 3.14f // f表示float
  cout<<f1 <<endl 
  system("pause")
  return 0
}

double(8字节)15-16位有效数字

科学计数法

int main(){
  float f1 = 3e2 // 3 * 10^2
  float f1 = 3e-2 // 3 * 0.1^2
  cout<<f1 <<endl 
  system("pause")
  return 0
}
  • 字符型

语法:char ch = ‘a’

C中字符型变量只占用1字节,

字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放到存储单元(a-97;A-65)

# include <iostream>
using namespace std;
int main()
{
	/*1.字符型变量创建方式
	* 2.字符型变量所占内存大小
	* 3.字符型变量常见错误
	* char ch2 = "b" 要用单引号
	* char ch2 = ‘abc’单引号内只能由一个字符
	* 4.字符型变量对应ASCII码
	*/
	char ch = 'c';
	cout << ch << endl;
	cout << sizeof(ch) << endl;
	cout << int(ch) << endl;
	system("pause");
	return 0;
}
  • 转义字符

作用:用来表示一些不能显示出来的ASCII字符

现阶段常用的转义字符有:\n \ \t

# include <iostream>
using namespace std;
#define Day 7
int main()
{
	//转义字符
	//换行符\n
	cout << "hello world\n" << endl;
	//反斜杠(\\)
	cout << "\\" << endl;
	//水平制表符\t 整齐地输出数据
	cout << "aaaa\t hello world" << endl;
	cout << "aaa\t hello world" << endl;
	cout << "aaaaaaa\t hello world" << endl;
	cout << "aaaaa\t hello world" << endl;
	system("pause");
	return 0;
}
  • 字符串型

1.C风格字符串:char 变量名[ ] = "字符串值"

2.C++风格字符串:string 变量名 = "字符串值"

# include <iostream>
using namespace std;
# include <string>
int main()
{
	char str[] = "hello world";
	// char字符串名[]
	// 等号后面用双引号
	string str2 = "hello world";
	// 包含一个头文件
	cout << str << endl;
	cout << str2 << endl;
	system("pause");
	return 0;
}
  • 布尔类型bool
# include <iostream>
using namespace std;
int main()
{
	bool flag = true;
	cout << flag << endl;
	system("pause");
	return 0;
}
  • 数据的输入

关键字cin

语法:cin>>输入

# include <iostream>
using namespace std;
# include <string>
int main()
{
	//1.int
	int s = 0;
	cout << "请给int量赋值:" << endl;
	cin >> s;
	cout << "int变量s=" << s << endl;
	//2.float
	float f = 3.14f;
	cout << "请给float变量赋值:" << endl;
	cin >> f;
	cout << "float变量f=" << f << endl;
	//3.char
	char ch = 'a';
	cout << "请给字符型变量ch赋值" << endl;
	cin >> ch;
	cout << "字符型变量ch=" << ch << endl;
	// str
	string str = "hello";
	cout << "请给字符串型变量赋值" << endl;
	cin >> str;
	cout << "字符串变量str=" << str << endl;
	// bool
	bool flag = false;
	cout << "布尔类型flag赋值" << endl;
	cin >> flag;
	cout << "flag=" << flag << endl;
	system("pause");
	return 0;
}

C++运算符

算数运算符

# include <iostream>
using namespace std;
# include <string>
int main()
{
	//加减乘除
	int n1 = 10;
	int n2 = 3;
	cout << n1 + n2 << endl;
	cout << n1 - n2 << endl;
	cout << n1 * n2 << endl;
	cout << n1 / n2 << endl;
	//取模运算(求余数)
	cout << n1 % n2 << endl;
	// 前置递增,先让变量+1再进行表达式运算
	int n3 = ++n1 * 10;
	cout << "n1=" << n1 << endl;
	cout << "n3=" << n3 << endl;
	// 后置递增,先进行表达式运算,后让变量+1
	int n4 = 10;
	int n5 = n4++ * 10;
	cout << "n4=" << n4 << endl;
	cout << "n5=" <<n5<< endl;
	// 前置递减
	int n6 = 10;
	int n7 = --n6;
	cout << "n6=" << n6 << endl;
	cout << "n7=" << n7 << endl;
	// 后置递减
	int n8 = 10;
	int n9 = n8--;
	cout << "n8=" << n8 << endl;
	cout << "n9=" << n9 << endl;

	system("pause");
	return 0;
}

赋值运算符

+=; -=;*=;/=;%=

比较运算符

==;!=; <; >; <=; >=

逻辑运算符

!(非); &&(与); ||(或)

# include <iostream>
using namespace std;
int main()
{
	//逻辑运算符非!
	int b = 10;
	cout << !b << endl; //0
	cout << !!b << endl; //1
	// &&
	int a = 10;
	cout << (a && b) << endl; //1
	// ||
	int c = 0;
	cout << (a||b) << endl; //1
	cout << (a||c) << endl; //1

	system("pause");
	return 0;
}

C++程序流程结构

顺序结构、选择结构、循环结构

if语句

*单行格式的if语句*

# include <iostream>
using namespace std;
int main()
{
	//选择结构 单行if语句
	//用户输入一个分数,判断考上大学否
	int score = 0;
	//用户输入一个分数
	cout << "请输入一个分数:" << endl;
	cin >> score;
	//打印这个分数
	cout << "您输入的分数是:" << score << endl;
	//判断这个分数是否超过一本线
	if(score>500)
	{
		cout << "恭喜您考上了一本大学" << endl;
	}
	//if条件后面不要加分号
	system("pause");
	return 0;
}

*多条件的if语句*

# include <iostream>
using namespace std;
int main()
{
	//选择结构多条件if语句
	//用户输入一个分数,判断考上的大学
	int score = 0;
	//用户输入一个分数
	cout << "请输入一个分数:" << endl;
	cin >> score;
	//打印这个分数
	cout << "您输入的分数是:" << score << endl;
	//判断这个分数是否超过一本线,打印考上一本,否则未考上大学
	if(score>600)
	{
		cout << "恭喜您考上了一本大学!" << endl;
	}
	else if(score>500)
	{
		cout << "恭喜您考上了二本大学!" << endl;
	}
	else if(score>400)
	{
		cout << "恭喜您考上了三本大学!" << endl;
	}
	else 
	{
		cout << "分数不满足,请再接再厉!" << endl;
	}
	//if条件后面不要加分号
	system("pause");
	return 0;
}

*嵌套格式的if语句*

# include <iostream>
using namespace std;
int main()
{
	//选择结构多条件if语句
	//用户输入一个分数,判断考上的大学
	int score = 0;
	//用户输入一个分数
	cout << "请输入一个分数:" << endl;
	cin >> score;
	//打印这个分数
	cout << "您输入的分数是:" << score << endl;
	//判断这个分数是否超过一本线,打印考上一本,否则未考上大学
	if(score>600)
	{
		cout << "恭喜您考上了一本大学!" << endl;
		if(score>700)
		{
			cout << "您能考上北京大学!" << endl;
		}
		else if(score>650)
		{
			cout << "您能考上清华大学!" << endl;
		}
		else
		{
			cout << "您能考上人民大学!" << endl;
		}
	}
	else if(score>500)
	{
		cout << "恭喜您考上了二本大学!" << endl;
	}
	else if(score>400)
	{
		cout << "恭喜您考上了三本大学!" << endl;
	}
	else 
	{
		cout << "分数不满足,请再接再厉!" << endl;
	}
	//if条件后面不要加分号
	system("pause");
	return 0;
}

*多行格式的if语句*

# include <iostream>
using namespace std;
int main()
{
	//选择结构 单行if语句
	//用户输入一个分数,判断考上大学否
	int score = 0;
	//用户输入一个分数
	cout << "请输入一个分数:" << endl;
	cin >> score;
	//打印这个分数
	cout << "您输入的分数是:" << score << endl;
	//判断这个分数是否超过一本线,打印考上一本,否则未考上大学
	if(score>500)
	{
		cout << "恭喜您考上了一本大学!" << endl;
	}
	else 
	{
		cout << "您未考上一本大学,请再接再厉!" << endl;
	}
	//if条件后面不要加分号
	system("pause");
	return 0;
}

小猪称体重案例

# include <iostream>
using namespace std;
int main()
{
	//三只小猪称体重,判断哪知最重
	//创建三只小猪的体重变量
	int num1 = 0;
	int num2 = 0;
	int num3 = 0;
	//用户输入三只小猪的重量
	cout << "请输入小猪A的体重:" << endl;
	cin >> num1;

	cout << "请输入小猪B的体重:" << endl;
	cin >> num2;

	cout << "请输入小猪C的体重:" << endl;
	cin >> num3;

	cout << "小猪A的体重为:" << num1 <<endl;
	cout << "小猪B的体重为:" << num2 << endl;
	cout << "小猪C的体重为:" << num3 << endl;

	//判断谁最重
	//先比较A和B
	if (num1 > num2)
	{
		if (num1 > num3)
		{
			cout << "小猪A最重。" << endl;
		}
		else
		{
			cout << "小猪C最重。" << endl;
		}
	}
	else
	{
		if (num2 > num3)
		{
			cout << "小猪B最重。" << endl;
		}
		if (num2 < num3)
		{
			cout << "小猪C最重。" << endl;
		}
	}
	system("pause");
	return 0;
}
三目运算符

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

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

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

# include <iostream>
using namespace std;
int main()
{
	//三目运算符?:
	//创建三个变量abc将ab比较,将较大的赋值给c

	int a = 10;
	int b = 20;
	int c = 0;
	c = (a > b) ? a : b;
	cout << c << endl;

	//在C++中三目运算符返回的是变量,可以继续赋值
	(a < b ? a : b) = 100;
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	system("pause");
	return 0;
}
switch语句

执行多条件分支语句

判断时候只能是整型或者字符型,不可以是一个区间

如果不写break程序会一直向下走

优点是运行效率高,结构清楚。

# include <iostream>
using namespace std;
int main()
{
	//switch语句
	//给电影打分
	//提示用户进行打分
	int score = 0;
	cout << "请您给电影进行打分:" << endl;
	cin >> score;
	cout << "您打的分数是:" << score << endl;

	switch (score)
	{
	case 10:
		cout << "您认为这是经典电影。" << endl;
		break; //推出当前分支
	case 9:
		cout << "您认为这是经典电影。" << endl;
		break;
	case 8:
		cout << "您认为电影非常好。" << endl;
		break;
	case 7:
		cout << "您认为电影很好。" << endl;
		break;
	case 6:
		cout << "您认为电影及格。" << endl;
		break;
	default:
		cout << "您认为这是一个烂片。" << endl;
	}
	system("pause");
	return 0;
}

循环结构

while循环语句

while(循环条件)(循环语句)

# include <iostream>
using namespace std;
int main()
{
	//while语句
	//在屏幕中打印0-9这10个数字
    //避免死循环
	int num = 0;
	while (num < 10)
	{
		cout << num << endl;
		num++;
	}
	system("pause");
	return 0;
}

猜数字案例

# include <iostream>
using namespace std;
# include <ctime>
int main()
{
	//添加随机数种子 利用当前系统时间生成随机数,防止每次随机数都一样
	srand((unsigned int)time(NULL));
	//系统生成一个随机数
	int num = rand() % 100 + 1;
	//cout << num << endl;
	
	while (1)
	{
		int val = 0;
		cout << "请输入猜测值:" << endl;
		cin >> val;

		//判断玩家的猜测
		if (val > num)
		{
			cout << "猜测过大。" << endl;
		}
		else if (val < num)
		{
			cout << "猜测过小." << endl;
		}
		else
		{
			cout << "恭喜您猜对了!" << endl;
			break;
		}
	}

	system("pause");
	return 0;
}

do……while循环语句

do(循环语句)while(循环条件)

# include <iostream>
using namespace std;
int main()
{
	int num = 0;

	do
	{
		cout << num << endl;
		num++;
	} 
	while (num<10);
	system("pause");
	return 0;
}

水仙花数案例

# include <iostream>
using namespace std;
int main()
{
	//先打印所有的三位数
	int num = 100;
	do
	{
		//判断水仙花数
		int a = 0; //个位数
		int b = 0; //十位数
		int c = 0; //百位数
		
		a = num % 10;
		b = num / 10 % 10;
		c = num / 100;

		if (a * a * a + b * b * b + c * c * c == num) //是水仙花才打印
		{
			cout << num << endl;
		}
		num++;
	} 
	while (num < 1000);
	system("pause");
	return 0;
}
for 循环

语法:for(起始表达式;条件表达式;末尾循环体){循环语句} //起始表达式只执行一次

# include <iostream>
using namespace std;
int main()
{
	//for 循环语句
	//打印从0到9
	for (int i = 0; i < 10; i++) 
	{
		cout << i << endl;
	}
	system("pause");
	return 0;
}

敲桌子案例

# include <iostream>
using namespace std;
int main()
{
	//敲桌子案例
	//0-100个数,遇到个位数是7(%10=7)、7的倍数(%7=0)、十位数是7(/10=7)不输出,改为输出“敲桌子”
	for (int i = 0; i <= 100; i++) 
	{
		if (i % 10 == 7 || i % 7 == 0 || i / 10 == 7)
		{
			cout << "敲桌子" << endl;
		}
		else
		{
			cout << i << endl;
		}		
	}
	system("pause");
	return 0;
}
嵌套循环
# include <iostream>
using namespace std;
int main()
{
	//利用嵌套循环打印星图
	//外层循环
	//外层执行一次,内层循环一周
	for (int i = 0; i < 10; i++)
	{
		//内层循环
		for (int j = 0; j < 10; j++)
		{
			cout << "*" ;
		}
		cout << endl;
	}
	
	system("pause");
	return 0;
}

案例乘法口诀表

列数 * 行数 = 计算结果

列数 <= 行数

#include<iostream>
using namespace std;

int main()
{
	//打印乘法口诀
	for (int i = 1; i <= 9; i++) {
		// cout << i << endl;
		for (int j = 1; j <= i; j++) {
			cout << j<<"*"<< i << "=" << j * i<<"  ";
		}
		cout << endl;
	}
	system("pause");
	return 0;
}
跳转语句

break语句 作用:用于跳出选择结构或循环结构

  • 出现在switch条件语句中,作用是终止case并跳出switch

  • 出现在循环语句中,作用是跳出当前的循环语句

  • 出现在嵌套循环中,跳出最近的内层循环语句

    #include<iostream>
    using namespace std;
    
    int main()
    {
    	//break
    	//1.出现在switch中
    	cout << "请选择副本难度:" << endl;
    	cout << "1:简单;2:中等;3:困难" << endl;
    	int select = 0;
    	cin >> select;
    
    	switch (select)
    	{
    	//必须是case 1,这个1前面必须有空格
    	case 1:
    		cout << "您选择了普通难度" << endl;
    		break;
    	case 2:
    		cout << "您选择了中等难度" << endl;
    		break;
    	case 3:
    		cout << "您选择了困难难度" << endl;
    		break;
    	default:
    		break;
    	}
        
        //2.出现在循环中
    	for (int i = 0; i < 10; i++)
    	{
    		if (i == 5)
    		{
    			break;
    		}
    		cout << i << endl;
    	}
    
        //3.出现在嵌套循环中
    	for (int i = 0; i < 10; i++) {
    		for (int j = 0; j < 10; j++)
    		{
    			if (j == 5) {
    				break;
    			}
    			cout << "* ";
    		}
    		cout << endl;
    	}
    	system("pause");
    	return 0;
    }
    
跳转语句-continue语句

*作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环*

代表这次循环在continue处戛然而止

#include<iostream>
using namespace std;

int main()
{
	//continue
	for (int i = 0; i <= 100;i++)
	{
		//如果是奇数输出,偶数不输出
		if (i % 2 == 0)
		{
			continue; //可以筛选条件,执行到此不再向下执行,执行下一次循环
		}
		cout << i << endl;
	}
	system("pause");
	return 0;
}
跳转语句-goto语句

*作用:无条件跳转语句*

C++数组

一维数组基础知识

数组 *就是一个集合,里面存放了相同类型的数据元素*

特点:相同类型;内存连续

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

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

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

#include<iostream>
using namespace std;

int main()
{
	//数组
	/*1.  `数据类型 数组名 [数组长度];`

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

    `数据类型 数组名[] = {值1, 值2,……}`
*/
	//1
	int arr[5];
    //利用下表对数组元素进行赋值
	arr[0] = 1;
	arr[1] = 2;
	arr[2] = 4;
	arr[3] = 5;
	arr[4] = 6;
	cout << arr[0] << endl;
	system("pause");
	return 0;
}

	//2.
	// 如果不初始化,系统会用0填充
	int arr2[5] = { 10, 20, 30, 40 };
	for (int i = 0; i < 5; i++)
	{
		cout << arr2[i] << endl;
	}


	//3
    //必须初始化所有数组元素
	int arr3[] = { 10, 20, 30 };

一维数组数组名

名称的用途:可以统计整个数组在内存中的长度;可以获取数组在内存中的首地址;

数组名是常量,不可以进行赋值操作

#include<iostream>
using namespace std;

int main()
{
	//一维数组名称
	int arr[5];
	cout << sizeof(arr) << endl; //20(5*4)
	cout << sizeof(arr[0]) << endl; //4
	cout << sizeof(arr) / sizeof(arr[0]) << endl; //5
	cout << "数组的首地址是:" << (int)arr << endl; //(int)将地址转为10进制查看
	cout << "数组中第一个元素地址为:" << (int) & arr[0] << endl; //&取地址符号

	system("pause");
	return 0;
}
数组案例-五只小猪称体重
#include<iostream>
using namespace std;

int main()
{
	//创建五只小猪体重的数组
	int arr[5] = { 100, 400, 300, 520, 700 };
	//从数组中找到最大值
	int max = 0; //先认定最大值0
	for (int i = 0; i < 5; i++)
	{
		if (arr[i] > max) {
			max = arr[i];
		} //便历经所有小猪体重,如果比max大,就更新max
	}
	//打印最大值
	cout <<"max="<< max << endl;
	system("pause");
	return 0;
}
数组案例-数组元素逆置
#include<iostream>
using namespace std;

int main()
{
	//实现数组元组中的元素逆置
	int arr[] = { 1, 3, 2, 5, 4, 6 };
	cout << "逆置前的结果是:" << endl;
	for (int i =0 ; i <6; i++) {
		cout << arr[i];
	}
	cout << endl;

	int start = 0; //起始下标
	int end = sizeof(arr) / sizeof(arr[0]) - 1; //终止下标

	while (start < end)
	{
		int tmp = arr[start];
		arr[start] = arr[end];
		arr[end] = tmp;

		start++;
		end--;
	}

	cout << "逆置后的结果是:" << endl;
	for (int j = 0; j < 6; j++) {
		cout << arr[j];
	}
	cout << endl;
	system("pause");
	return 0;
}
冒泡排序
最常用的排序算法,对数组内元素进行排序
#include<iostream>
using namespace std;

int main()
{
	//利用冒泡排序实现升序序列
	int arr[] = { 4, 2, 5, 6, 7, 8, 1, 10 };
	cout << "排序之前:" << endl;
	for (int i = 0; i < 8; i++)
	{
		cout << arr[i]<<"  ";
	}
	cout << endl;
	//排序轮数=元素个数-1
	//每轮对比次数=元素个数-排序轮数-1
	for (int i = 0; i < 8 - 1; i++)
	{
		//内存循环比较相邻数据
		for (int j = 0; j < 8 - i - 1; j++)
		{
			//如果前面的元素大于相邻的后一个元素,则交换这两个元素
			if (arr[j] > arr[j + 1]) {
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}

	//排序后结果
	cout << "排序之后:" << endl;
	for (int i = 0; i < 8; i++)
	{
		cout << arr[i] << "  ";
	}
	cout << endl;
	system("pause");
	return 0;
}
二维数组基础知识

二维数组就是在一维数组上,多加一个维度。

定义方式:

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

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

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

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

    #include<iostream>
    using namespace std;
    
    int main()
    {
    	// 二维数组
    	/*
    	数据类型 数组名[ 行数 ][ 列数 ];
        数据类型 数组名[ 行数 ][ 列数 ] = { {数据1, 数据2},{数据3, 数据4}};
        数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4 } ;
        数据类型 数组名[][ 列数 ] = { 数据1,数据2,数据3,数据4 } ;
    	*/
    	//1.
    	int arr[2][3];
    	// 赋值
    	arr[0][0] = 1;
    	arr[0][1] = 2;
    	arr[0][2] = 3;
    	arr[1][0] = 4;
    	arr[1][1] = 5;
    	arr[1][2] = 6;
    	//print
    	for (int i = 0; i < 2; i++)
    	{
    		for (int j = 0; j < 3; j++)
    		{
    			cout << arr[i][j] << endl;
    		}
    	}
    	cout << arr << endl;
    	//2
    	int arr2[2][3] =
    	{
    		{1, 2, 3},
    		{4, 5, 6}
    	};
    	// print
    	for (int i = 0; i < 2; i++)
    	{
    		for (int j = 0; j < 3; j++)
    		{
    			cout << arr2[i][j] << " ";
    		}
    		cout << endl;
    	}
    	
    	// 3 
    	int arr3[2][3] = { 1, 2, 3, 4, 5, 6 };
    	// print
    	for (int i = 0; i < 2; i++)
    	{
    		for (int j = 0; j < 3; j++)
    		{
    			cout << arr3[i][j] << " ";
    		}
    		cout << endl;
    	}
    
        //4
    	int arr4[][3] = { 1, 2, 3, 4, 5, 6 };
    	// print
    	for (int i = 0; i < 2; i++)
    	{
    		for (int j = 0; j < 3; j++)
    		{
    			cout << arr4[i][j] << " ";
    		}
    		cout << endl;
    	system("pause");
    	return 0;
    }
    

二维数组名称

作用:查看二维数组所占内存空间;获取二维数组首地址

#include<iostream>
using namespace std;

int main()
{
	// 查看占用空间大小
	int arr[2][3] = {
		{1,2,3},
		{4, 5, 6}
	};
	cout << "二维数组占用内存为" << sizeof(arr) << endl; //24(4*6)
	cout << "二维数组一行占用内存为" << sizeof(arr[0]) << endl; //12
	cout << "二维数组第一个元素占用内存为" << sizeof(arr[0][0]) << endl; //4
	cout << "arr的行数" << sizeof(arr) / sizeof(arr[0]) << endl;
	cout << "arr的列数" << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;
	// 查看二维数组的地址
	cout << "arr首地址为" << (int)arr << endl;
	cout << "arr第一个元素的地址" << (int)&arr[0][0] << endl; //&取址符号
	system("pause");
	return 0;
}
数组案例—考试成绩统计
#include<iostream>
using namespace std;
#include <string>
int main()
{
	// 1.创建二维数组,保存成绩(3,3)
	int score[3][3] =
	{
		{100, 100, 100},
		{60, 50, 100},
		{60, 70, 50}
	};
	string name[3] = {"张三","李四","王五"};
	// 2.统计考试成绩,让每行三列相加
	for (int i = 0; i < 3; i++)
	{
		int sum = 0;
		for (int j = 0; j < 3; j++)
		{
			sum += score[i][j];
		}
		cout <<name[i] << "的总成绩为:" << sum << endl;
	}
	system("pause");
	return 0;
}

C++函数

函数定义

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

定义的5步骤:

  • 返回值类型

  • 函数名

  • 参数列表

  • 函数体语句

  • return表达式

    #include<iostream>
    using namespace std;
    #include <string>
    int main()
    {
    	system("pause");
    	return 0;
    }
    
    // 实现一个加法函数
    int add(int num1, int num2)
    {
    	int sum = num1 + num2;
    	return sum;
    }
    
函数调用
#include<iostream>
using namespace std;
#include <string>

// 实现一个加法函数
// num1和num2只是形式上的参数,简称形参
int add(int num1, int num2)
{
	int sum = num1 + num2;
	return sum;
}


int main()
{
	//main 中调用add函数
	int a = 10;
	int b = 20;
	// 函数调用语法:函数名称(参数)
	// a和b称为实际参数,简称实参
	// 调用的时候,实参的值传给形参
	int c = add(a, b);
	cout << c << endl;

	system("pause");
	return 0;
}

值传递

#include<iostream>
using namespace std;

// 值传递
// 定义函数,实现两个数字交换
// 如果一个函数不需要返回值,声明的时候可以写void
void swap(int num1, int num2)
{
	cout << "交换前:" <<" num1= "<< num1<<" num2= "<<num2<< endl;
	int tmp = num1;
	num1 = num2;
	num2 = tmp;
	cout << "交换后:" << " num1= " << num1 << " num2= " << num2 << endl;
	return;
}


int main()
{
	int a = 10;
	int b = 20;
	cout << "a = " << a << "b = " << b << endl;
	swap(a, b);
	// 当我们做值传递的时候,函数的形参发生改变,并不会影响实参
	cout << "a = " << a << "b = " << b << endl;


	system("pause");
	return 0;
}

==**值传递时,形参修饰不了实参。**==

函数的常见样式
  • 无参无返

  • 有参无返

  • 无参有返

  • 有参有返

    #include<iostream>
    using namespace std;
    
    // 1.无参无返
    void test01()
    {
    	cout << "this is test01" << endl;
    }
    
    
    // 2. 
    void test02(int a)
    {
    	cout << "this is test02:" << a << endl;
    }
    
    // 3
    int test03()
    {
    	cout << "this is test03" << endl;
    	return 1000;
    }
    
    // 4
    int test04(int a, int b)
    {
    	cout << "this is test04" << endl;
    	return a + b;
    }
    int main()
    {
    	test01();
    	test02(100);
    	int num1 = test03();
    	cout << num1<<endl;
    	int num2 = test04(100, 2000);
    	cout << num2 << endl;
    	system("pause");
    	return 0;
    }
    
函数声明

如果函数定义在main函数后面,前面必须写函数声明。

声明可以有多次,但是定义只能有一次。

#include<iostream>
using namespace std;

// 提前告诉编译器函数的存在,可以利用函数的声明
// 函数的声明
int max(int a, int b);

int main()
{
	cout << max(1000, 20) << endl;
	system("pause");
	return 0;
}

// 定义
int max(int a, int b)
{
	return a > b ? a : b;
}
函数的分文件编写

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

步骤:

  • *创建后缀名为.h的头文件*

  • *创建后缀名为.cpp的源文件*

  • *在头文件中写函数的声明*

  • *在源文件中写函数的定义*

    注意头文件在调用时候,必须include才可使用

    #pragma once
    // 头文件
    // 声明
    void swap(int a, int b);
    
# include <iostream>
using namespace std;
# include "swap.h"
// 源文件
// 定义
void swap(int a, int b)
{
	int tmp = b;
	b = a;
	a = tmp;
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
}
# include <iostream>
using namespace std;
# include"swap.h"
// 包含主函数的源文件
int main()
{
	int a = 10;
	int b = 20;
	swap(a, b);
	system("pause");
	return 0;
}

C++指针(p)

指针基础知识

作用:可以通过指针间接访问内存(指针可以保存一个地址)

内存编号是从0开始记录的,一般用十六进制数字表示

可以利用指针变量保存地址

# include <iostream>
using namespace std;

int main()
{
	// 定义指针
	int a = 10;
	// 语法定义
	int* p;
	//让指针记录变量a的地址
	p = &a;
	cout << "a的地址是:" << &a << endl;
	cout << "指针p是:" << p << endl;

	// 使用指针
	// 可以通过解引用的方式来找到指针指向的内存
	// *p
	*p = 1000;
	cout << "a= " << a << endl;
	cout << "*p= " << *p << endl;

	system("pause");
	return 0;
}

指针所占的内存空间

# include <iostream>
using namespace std;

int main()
{
	// 指针所占的内存空间
    //  在64位操作系统中,指针占用8个字节,与数据类型无关
    // 在32位操作系统中,指针占用4个字节
	int a = 10;
	int* p = &a;
	cout << "sizeof(int*): " << sizeof(int*) << endl; //8
	cout << "sizeof(p) " << sizeof(p) << endl;
	cout << "sizeof(double*) " << sizeof(double*) << endl;  //8

	system("pause");
	return 0;
}
空指针

指针变量指向内存中编号为0的空间的指针

作用:初始化指针变量(不知道指针指哪可以先指这)

空指针指向的内存是不可以更改的

野指针

指针变量指向非法的内存空间

# include <iostream>
using namespace std;

int main()
{
	// 野指针
	// 在程序中避免出现
	int* p = (int*)0x1100;


	system("pause");
	return 0;
}
const修饰指针
  • const修饰指针——常量指针(const *)

  • const修饰常量——指针常量

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

    我的理解:const修饰之后,指针不是不能改,而是不能通过某种方式改,比如指针常量,指针指向的值可以改,但是不能通过改变其地址的方式修改。

 # include <iostream>
using namespace std;

int main()
{
	// const *
	int a = 10;
	int b = 20;
	const int* p = &a;
	cout << *p << endl; // 10
	// 指针指向的值不可以更改,指针指向可以更改
	// *p = 20;错误
	p = &b; // true
	cout << *p << endl; // 20

	// * const
	int* const p2 = &a;
	cout << *p2 << endl; // 10
	// 指针的指向不可以改,指针指向的值可以改
	// p2 = &b; false
	*p2 = 20;
	cout << *p2 << endl; // 20
	cout << "a= " << a << endl; // a=20

	// const修饰指针和变量
	const int* p3 = &a;
	// *p3 = b; False
	// *p3 = 20; False

	system("pause");
	return 0;
}
指针和数组

利用指针访问数组元素

# include <iostream>
using namespace std;

int main()
{
	// 指针和数组
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* p = arr; //arr是数组的首地址
	cout << "arr的第一个元素是:" << *p << endl;
	p++; // 往后偏移4个字节
	cout << "arr的第二个元素是:" << *p << endl;
	cout << "利用指针遍历数组" << endl;

	int* p2 = arr;
	for (int i = 0; i < 10;i++)
	{
		cout << *p2 << endl;
		p2++;
	}
	system("pause");
	return 0;
}
指针和函数

利用指针作为函数参数,可以修改实参的值

# include <iostream>
using namespace std;

void swap(int a, int b)
{
	int tmp = b;
	b = a;
	a = tmp;
	cout << "swap a=" << a << endl;
	cout << "swap b=" << b << endl;
}

void swap02(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

int main()
{
	// 指针和函数
	// 1.值传递
	int a = 10;
	int b = 20;
	swap(a, b);
	cout << "a=" << a << endl; //10
	cout << "b=" << b << endl; //20
	// 值传递不能修饰实参
	
	// 地址传递
	swap02(&a, &b);
	cout << "a=" << a << endl; //20
	cout << "b=" << b << endl; //10
	// 地址传递可以修饰实参
	system("pause");
	return 0;
}
指针、数组、函数、案例

—封装一个函数,利用冒泡排序,实现对数组的升序排序

*我的理解:函数的参数是数组的时候,要把数组当作整体,用地址访问数组,只需知道首地址和数组的长度。*

# include <iostream>
using namespace std;

//创建函数实现冒泡排序 参数1 数组的首地址 参数2 数组的长度
void bubbleSort(int* arr,int len)
{
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = 0; j < len - i - 1; j++)
		{
			if (arr[j] > arr[j + 1] )
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;

			}
		}
	}
}

// 打印数组
void printArray(int *arr,int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << endl;
	}
}


int main()
{
	//1.创建数组
	int arr[10] = { 4, 3, 6, 9, 1, 2, 10, 8, 7, 5 };

	int len = sizeof(arr) / sizeof(arr[0]);
	//创建函数实现冒泡排序
	bubbleSort(arr, len);
	//展示结果
	printArray(arr, len);

	return 0;
}

C++结构体

结构体基础知识

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

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

# include<iostream>
using namespace std;
# include<string>


// 1、创建学生数据类型:包括(姓名、年龄、分数)
// 自定义的数据类型,一些类型集合组成的一个类型
struct Student
{
	// 成员列表
	
	// 姓名
	string name;
	// 年龄
	int age;
	// 分数
	int score;
}s3;  //顺便创建结构体变量

// 2、通过学生类型创建具体学生

int main()
{
	// 2.1 struct Student s1
	// 创建变量的时候struct关键字可以省略
    Student s1;
	s1.name = "张三";
	s1.age = 18;
	s1.score = 500;

	cout << "姓名: " << s1.name << "年龄: " << s1.age << "成绩: " << s1.score << endl;

	// 2.2 struct Student s2 = {...}
	struct Student s2 = {"张三", 19, 100};
	cout << " 姓名: " << s2.name << " 年龄: " << s2.age << " 成绩: " << s2.score << endl;

	// 2.3 在定义结构体时顺便创建结构体变量
	s3.name = "张三";
	s3.age = 18;
	s3.score = 500;
	cout << " 姓名: " << s3.name << " 年龄: " << s3.age << " 成绩: " << s3.score << endl;
	system("pause");
	return 0;
}
结构体数组

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

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

# include<iostream>
using namespace std;
# include<string>


// 1、定义结构体

struct Student
{
	// 成员列表

	// 姓名
	string name;
	// 年龄
	int age;
	// 分数
	int score;
};




int main()
{
	// 2、创建结构体数组
	struct Student stuArray[3] =
	{
		{"张三", 18, 200},
		{"李四", 19, 150},
		{"王五", 16, 300}
	};

	// 3、给结构体数组中的元素赋值
	stuArray[2].name = "赵六";
	stuArray[2].age = 30;

	// 4、遍历这个数组
	for (int i = 0; i < 3; i++)
	{
		cout << "name: " << stuArray[i].name << " age: " << stuArray[i].age << " score: " << stuArray[i].score << endl;

	}
	system("pause");
	return 0;
}
结构体指针

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

->可以通过结构体指针访问结构体属性

# include<iostream>
using namespace std;
# include<string>


// 1、定义结构体

struct Student
{
	// 成员列表

	// 姓名
	string name;
	// 年龄
	int age;
	// 分数
	int score;
};


int main()
{
	// 2、创建结构体变量
	struct Student s1 = { "张三", 19, 200 };
	// 3、通过指针指向结构体变量
	struct Student* p = &s1;
	// 4、通过指针访问结构体变量中的数据
	cout << "name: " << p->name << " age: " << p->age << " score: " << p->score << endl;
	system("pause");
	return 0;
}
结构体嵌套结构体

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

# include<iostream>
using namespace std;
# include<string>


struct student
{
	string name;
	int age;
	int score;
};

struct teacher 
{
	string name;
	int id;
	int age;
	student stu;
};


int main()
{
	teacher t;
	t.name = "Mr. Wang";
	t.age = 36;
	t.id = 482133;
	t.stu.name = "张三";
	t.stu.age = 19;
	t.stu.score = 500;
	cout << t.stu.score << endl;
	system("pause");
	return 0;
}
结构体做函数参数

值传递 and 地址传递

地址传递可以修饰实参

# include<iostream>
using namespace std;
# include<string>


struct student
{
	string name;
	int age;
	int score;
};

void printStudent1(student s)
{
	cout << "子函数中的name: " << s.name << " 子函数1中的age:" << s.age << " 子函数1中的score:" << s.score<<endl;
}

void printStudent2(student *p)
{
	p->name = "李四";
	cout << "子函数2中的name:" << p->name << endl;
}

int main()
{
	student s;
	s.name = "张三";
	s.age = 19;
	s.score = 200;
	//printStudent1(s);
	printStudent2(&s);
	cout << "main中的name:" << s.name << endl;
	system("pause");
	return 0;
}
结构体中const使用场景

防止误操作

将函数中的形参改为指针,可以减少内存空间,而且不会复制新的副本出来。为了防止函数中对实参的写操作,需要添加const。

# include<iostream>
using namespace std;
# include<string>


struct student
{
	string name;
	int age;
	int score;
};

void printStudent2(const student *p)
{
	// p->name = "李四";  操作失败,不可修改
	cout << "子函数2中的name:" << p->name << endl;
}

int main()
{
	student s;
	s.name = "张三";
	s.age = 19;
	s.score = 200;
	//printStudent1(s);
	printStudent2(&s);
	cout << "main中的name:" << s.name << endl;
	system("pause");
	return 0;
}
结构体案例

案例介绍:

设计学生和 老师结构体,在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员;

学生结构体中有姓名、考试分数、创建数组存放3名老师,通过函数给每个老师及所带的学生赋值。

# include<iostream>
using namespace std;
# include<string>
# include<ctime>

// 创建学生结构体
struct Student
{
	string sname;
	int score;
};

// 创建教师结构体
struct Teacher
{
	string tname;
	struct Student stuArray[5];
};

// 定义为结构体数组赋值的函数
void allocateSpace(struct Teacher tArray[], int len)
{

	string nameSeed = "ABCDE";
	for (int i = 0; i < len; i++)
	{
		// 姓名赋值
		tArray[i].tname = "Teacher_";
		tArray[i].tname += nameSeed[i];
		// stu数组赋值
		for (int j = 0; j < 5; j++)
		{
			tArray[i].stuArray[j].sname = "Stu_";
			tArray[i].stuArray[j].sname += nameSeed[j];
			int random = rand() % 61 + 40; //40-100之间的随机数
			tArray[i].stuArray[j].score = random;
		}
	}
}


// 定义打印结构体数组中内容的函数
void printInfo(struct Teacher tArray[], int len)
{
	for (int i = 0; i < len; i++)
	{
		cout << "tname= " << tArray[i].tname << endl;
		for (int j = 0; j < 5; j++)
		{
			cout << "\tsname= " << tArray[i].stuArray[j].sname 
				<< "   score= " << tArray[i].stuArray[j].score << endl;  // \t是缩进字符

		}
	}
}

int main()
{
	// 完全随机
	srand((unsigned int)time(NULL));
	// 创建teacher结构体数组
	struct Teacher tArray[3];
	// 调用赋值函数
	int len = sizeof(tArray) / sizeof(tArray[0]);
	allocateSpace(tArray, len);
	// 调用打印函数
	printInfo(tArray, len);
	system("pause");
	return 0;
}

C++核心编程

面向对象编程技术详细讲解

内存分区模型
代码区

存放函数体的二进制代码,由操作系统进行管理的(特点:存放二进制的机器指令;共享;只读

全局区

存放全局变量和静态变量以及常量

# include<iostream>
using namespace std;

// 创建全局变量
int g_a = 20;

const int c_a = 20;

int main()
{
	// 创建普通局部变量
	int a = 10;
	cout << "局部变量a的地址是:" << (int) & a << endl;
	cout << "全局变量b的地址是:" << (int) & g_a << endl;

	// 静态变量static关键字
	static int s_a = 50;
	cout << "静态变量s_a的地址是:" << (int) & s_a << endl;

	// 字符串常量
	cout << "字符串常量的地址是:" << (int)&"hello" << endl;

	// const
	cout << "全局常量的地址是:" << (int)&c_a << endl;
	const int l_c_a = 20;
	cout << "局部常量的地址是:" << (int)&l_c_a << endl;

	system("pause");
	return 0;
}

全局区数据:全局变量、常量(const修饰的全局变量、字符串常量)、静态变量

以上皆是程序运行前就存在的区域

栈区

由编译器自动分配释放,存放函数的参数值、局部变量等

注意<不要返回局部变量的地址,栈区开辟的数据由编译器自动释放>

# include<iostream>
using namespace std;

// 栈区数据的注意事项
// 不要返回局部变量的地址
int* func()
{
	int a = 10; // 局部变量,存放在栈区
	return &a; // 返回局部变量的地址
}

int main()
{
	int* p = func();
	cout << *p << endl; // 第一次可以打印正确的数字,是因为编译器做了保留
	cout << *p << endl; // 第二次这个数据就不再保留了
	cout << *p << endl;
	system("pause");
	return 0;
}
堆区

由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

# include<iostream>
using namespace std;


int* func()
{
	int* p = new int(10); 
	// new返回的该数据类型的指针
	return p;
}

void test01()
{
	int* p = func();
	cout << *p << endl;
	cout << *p << endl;
	cout << *p << endl;
	// 利用关键字delete进行释放
	delete p;
	// cout << *p << endl; 内存已经被释放,再次访问就是非法操作,会报错

}

void test02()
{
	int* arr = new int [10]; // 中括号代表10个元素
	for (int i = 0; i < 10; i ++ )
	{
		arr[i] = 100 + i;
	}
	for (int i=0; i < 10; i++)
	{
		cout << arr[i] << endl;
	}
	// 释放数组的时候要加[]
	delete[] arr;
}

int main()
{
	
	test01();
	test02();
	system("pause");
	return 0;
}

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

C++引用

引用的基本语法

作用:给变量起别名

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

# include<iostream>
using namespace std;
int main()
{
	int a = 0;
	int& b = a;
	cout << "b= " << b << endl; // 0
	b = 10;
	cout << "b= " << b << endl; // 10
	cout << "a= " << a << endl; // 10
	system("pause");
	return 0;
}
引用的注意事项

引用必须初始化;

初始化之后不可以再更改引用;

# include<iostream>
using namespace std;
int main()
{
	int a = 0;
	// int& b; 错误,未初始化变量b
	int& b = a;
	cout << "b= " << b << endl; // 0
	int c = 10;
	// int& b = c; 错误,不可以更改b
	b = c; // 正确,这是赋值操作
	cout << "a= " << a << endl; // 10
	system("pause");
	return 0;
}
引用做函数参数

引用传递也可以修饰实参,可以替代地址传递的方法。

# include<iostream>
using namespace std;

void Swap01(int a, int b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

void Swap02(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void Swap03(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int a = 20;
	int b = 10;
	// Swap01(a, b); // 值传递形参不修饰实参
	// Swap02(&a, &b); // 地址传递形参可以修饰实参
	Swap03(a, b); //引用传递也可以修饰实参
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

	system("pause");
	return 0;
}
引用做函数返回值
# include<iostream>
using namespace std;

// 返回局部变量引用
int& test01()
{
	int a = 10; //局部变量
	return a;
}

// 返回静态变量引用
int& test02()
{
	static int a = 10; //静态变量,存储在全局区
	return a;
}
int main()
{
	int &a = test01(); // 局部变量的引用不能返回
	cout << "a = " << a << endl; // a = 10,因为操作系统有保留
	cout << "a = " << a << endl; // 乱码,因为栈区的内存已经释放
	int& ref = test02();
	cout << "ref = " << ref << endl;
	cout << "ref = " << ref << endl;
	// 函数返回引用时,函数调用可以作为左值存在,可以被赋值
	test02() = 1000;
	cout << "ref = " << ref << endl;
	system("pause");
	return 0;
}
引用的本质

**引用的本质是一个指针常量**

常量引用

修饰形参,防止误操作

# include<iostream>
using namespace std;

void Print(const int& a)
{
	// a = 1000; // 如果不加const,会对实参进行修改;加const后就不再可以修改
	cout << "a = " << a << endl;
}

int main()
{
	int a = 10;
	// int& b = a;
	// int& c = 10; 错误,因为10是常量,引用必须指向一个合理的内存空间
	// 正确,因为系统对其进行了优化为 int tmp = 10; const int &c = tmp;
	// const int& c = 10;
	Print(a);

	system("pause");
	return 0;
}

C++函数高级

函数的默认参数

就是在定义函数的时候,给形参一个默认值。

注意事项:

如果某个位置已经有了默认参数,那么从这个位置后,从左到右都必须有默认值;

函数声明和函数定义只能有一者具有默认参数。

# include<iostream>
using namespace std;

// 函数默认参数
int func(int a = 10, int b = 30)
{
	return a + b;
}

int main()
{
	cout << func() << endl; // 40
	// 实参不传入值时候,就用默认参数,传入就用实参的值
	cout << func(20) << endl; // 50
	cout << func(20, 40) << endl; // 60
	system("pause");
	return 0;
}
函数的占位参数

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

# include<iostream>
using namespace std;

// 函数占位参数
// 占位参数也可以拥有默认参数
int func(int a = 10, int)
{
	return a;
}

int main()
{
	func(10, 10) // 这个int位的10要传入,但是在函数体中没有用,目前用不上
	system("pause");
	return 0;
}
函数重载的使用方法

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

函数重载需要满足条件:

  • 同一个作用域下

  • 函数名称相同

  • 函数参数类型不同或者个数不同或者顺序不同

    # include<iostream>
    using namespace std;
    
    // 函数重载
    void func()
    {
    	cout << "this is func()" << endl;
    }
    
    
    void func(int a)
    {
    	cout << "this is a func()" << endl;
    }
    
    void func(int a, double b)
    {
    	cout << "this is int a double b func()" << endl;
    }
    
    void func(double b, int a)
    {
    	cout << "this is double b int a func()" << endl;
    }
    
    // 无法重载的,函数返回不能作为函数重载的条件
    //int func(int a)
    //{
    //	return a;
    //
    //}
    
    
    int main()
    {
    	func();
    	func(10);
    	func(10, 3.14);
    	func(3.14, 10);
    
    	system("pause");
    	return 0;
    }
    
函数重载的注意事项
# include<iostream>
using namespace std;

// 函数重载注意事项
// 1、引用作为函数重载的参数

void func(int& a) // int &a = 10 是不合法的命令
{
	cout << "this is & func()" << endl;
}

void func(const int& a) // const int &a = 10 是合法的命令
{
	cout << "this is const & func()" << endl;
}

// 函数重载碰到默认参数
void func2(int a, int b = 10)
{
	cout << "" << endl;
}

void func2(int a)
{
	cout << "" << endl;
}
int main()
{
	int a = 10;
	func(a); // this is & func()
	func(10); // this is const & func()
	// func2(20); 出现二义性,不能调用函数
	// 函数重载尽量避开这种默认参数
	system("pause");
	return 0;
}

C++类和对象—封装

封装基础知识

C++面向对象的三大特性为:封装、继承、多态

封装的意义:将属性和行为作为一个整体,表现生活中的事物;将属性和行为加以权限控制。

# include<iostream>
using namespace std;

const double PI = 3.14;
//定义一个圆类,能够返回圆的周长
class Circle
{
	// 访问权限
	// 公共权限
public:

	// 属性,常用变量表示
	int m_r;

	// 行为,常用函数表示
	double calculateZC()
	{
		return 2 * PI * m_r;
	}

};

int main()
{
	//实例化一个圆,通过一个类创建一个类的对象
	Circle c;
	c.m_r = 10;
	cout << "圆的周长是:" << c.calculateZC() << endl;
	

	system("pause");
	return 0;
}
封装案例—设计学生类
# include<iostream>
using namespace std;
# include<string>

// 设计一个学生类,可以对学生的姓名和ID进行赋值,这个类也可以打印学生的姓名和ID
class Student
{
	// 访问权限
	// 公共权限
public:
    // 类中的属性和行为,统称为成员
	// 属性,常用变量表示 成员属性 成员变量
	string name;
	int ID;

	// 行为,常用函数表示 成员函数 成员方法
	void printName()
	{
		cout << "这个学生的姓名是:" << name << endl;
	}

	void printID()
	{
		cout << "这个学生的ID是:" << ID << endl;
	}
};

int main()
{
	Student s;
	s.name = "张三";
	s.ID = 202205;
	s.printName();
	s.printID();

	system("pause");
	return 0;
}
类和对象—封装—访问权限

类在设计时,可以把属性和行为放在不同的权限下,加以控制

public:类内可以访问,类外也可以访问; protected:类内可以访问,类外不可以访问; private:类内可以访问,类外不可以访问

# include<iostream>
using namespace std;
# include<string>
class Person
{
public:
	string name;

protected:
	string carID;

private:
	int money;

public:
	void printContent()
	{
		carID = "奔驰";
		money = 50;
		cout << "name= " << name << endl;
		cout << carID << endl;
		cout << money << "w" << endl;
	}
};

int main()
{
	Person p;
	p.name = "老王"; // public可以访问
	// p.carID = ""; 不可访问
	// p.money = ; 不可访问
	p.printContent();
	system("pause");
	return 0;
}
c++中class和struct的区别

struct和class的唯一区别在于默认的访问权限不同,struct默认为公有,class默认为私有。

# include<iostream>
using namespace std;

class C1
{
	int num; // 默认的权限
};

struct C2
{
	int num;
};
int main()
{
	C1 c1;
	// c1.num = 20; 默认的是私有权限不能访问
	C2 c2;
	c2.num = 20;
	cout << c2.num << endl;
	system("pause");
	return 0;
}
类和对象—封装—成员属性私有化

优点:将所有成员属性设置为私有,可以自己控制读写权限;对于写权限,我们可以检测数据的有效性。

# include<iostream>
using namespace std;
# include<string>

class Person
{
private:
	string p_name; //成员属性为私有,不可在class外不可直接访问
	int p_age;
public:
	// 让name可读可写,通过成员函数访问和修改
	void set_name(string name)
	{
		p_name = name;
	}

	string get_name()
	{
		return p_name;
	}
	
	// 让age可读可写,但是必须在合理范围内(检测数据有效性)
	int get_age()
	{
		return p_age;
	}

	void set_age(int age)
	{
		if (age < 10 || age > 100)
		{
			p_age = 0;
			cout << "输入值异常!" << endl;
			return;
		}
		p_age = age;
	}
};

int main()
{
	Person p;
	p.set_name("张三");
	p.set_age(150);
	cout << "这个人的姓名是:" << p.get_name() << endl;
	cout << "这个人的年龄是:" << p.get_age() << endl;
	p.set_age(18);
	cout << "这个人的年龄是:" << p.get_age() << endl;
	system("pause");
	return 0;
}
类和对象—立方体类案例

题目:设计立方体类,求出立方体的体积和面积,分别用全局函数和成员函数判断两个立方体是否相等。

# include<iostream>
using namespace std;
# include<string>

class Cube
{
private:
	int m_L; //长度
	int m_W; //宽度
	int m_H; //高度
public:
	// 设置这个立方体的长宽高
	void set_cube(int length, int width, int height)
	{
		m_L = length;
		m_W = width;
		m_H = height;

	}
	// 获取这个立方体的长宽高
	int get_cube()
	{
		return m_L, m_W, m_H;
	}
	// 计算这个立方体的面积
	int calS()
	{
		return 2 * (m_L * m_W + m_L * m_H + m_W * m_H);
	}
	// 计算这个立方体的体积
	int calV()
	{
		return m_L * m_W * m_H;
	}

	// 比较这个立方体和外面的立方体是否相等
	bool compare2(Cube c, int c_L, int c_W, int c_H)
	{
		if (c_L == m_L && c_W == m_W && c_H == m_H)
		{
			return true;
		}
		return false;
	}
};

// 比较两个立方体是否相等的函数
bool compare(Cube &c1, Cube &c2)
{

	if (c1.get_cube()== c2.get_cube())
	{
		return true;
	}
	return false;
}

int main()
{

	Cube c1;
	Cube c2;
	c2.set_cube(10, 10, 10);
	c1.set_cube(10, 10, 10);
	cout << "c1这个立方体的面积是:" << c1.calS() << endl; // 600
	cout << "c2这个立方体的体积是:" << c1.calV() << endl; // 1000

	// 用全局函数判断立方体是否相等
	bool ret = compare(c1, c2);
	if (ret)
	{
		cout << "全局函数识别结果是:这两个立方体相等" << endl;
	}
	else
		cout << "全局函数识别结果是:这两个立方体不相等" << endl;

	// 用成员函数判断立方体是否相等
	int c2L = 0; // 初始化变量
	int c2W = 0;
	int c2H = 0;
	c2L, c2W, c2H = c2.get_cube();  // 获取c2的长宽高
	bool set = c1.compare2(c2, c2L,c2W, c2H);
	if (ret)
	{
		cout << "成员函数识别结果是:这两个立方体相等" << endl;
	}
	else
		cout << "成员函数识别结果是:这两个立方体不相等" << endl;


	system("pause");
	return 0;
}

C++类和对象—对象特性

类和对象—对象特性—构造函数和析构函数

对象的初始化和清理由构造函数和析构函数解决,如果我们不提供构造和析构,编译器会提供,但是编译器提供的构造和析构函数是空实现。

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

语法:类名(){}

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

语法:~类名(){}

#include<iostream>
using namespace std;

// 对象的初始化和清理
//1、构造函数 进行初始化操作
class Person
{
public:
	//1.1构造函数
	// 没有返回值,不用写void
	// 函数名与类名相同
	// 构造函数可以有参数,可以发生重载
	// 创建对象的时候,构造函数会自动调用,且只调用一次
	Person()
	{
		cout << "Person构造函数的调用。" << endl;
	}
	// 1.2 析构函数
	// 不写void,没有返回值
	// 函数名和类名相同,前加~
	// 析构函数不可以有参数,不可以发生重载
	// 对象销毁前,会自动调用析构函数,而且只调用一次
	~Person()
	{
		cout << "Person析构函数的调用。" << endl;
	}
};


void test01()
{
	Person p; // 在栈区的数据,test01执行完毕后,自动释放
}


int main()
{
	test01();
	Person p; // 在main函数结束之后(按了enter之后)才会调用析构函数
	system("pause");
	return 0;
}
类和对象—对象特性—构造函数的分类及调用

两种分类方式:

按参数分为:有参构造和无参构造

按类型分为:普通构造和拷贝构造

三种调用方式:

括号法;显示法;隐式转换法。

#include<iostream>
using namespace std;

// 
// 按照参数分为有参和无参(默认构造)
// 按照类型分为普通和拷贝构造
class Person
{
public:
	Person()
	{
		cout << "Person的无参构造函数的调用。" << endl;
	}

	Person(int a)
	{
		age = a;
		cout << "Person的有参构造函数调用。" << endl;
	}

	Person(const Person &p)
	{
		// 将传入的人身上的所有属性,拷贝到自己身上
		age = p.age;
		cout << "Person的拷贝构造函数调用。" << endl;
	}

	
	~Person()
	{
		cout << "Person析构函数的调用。" << endl;
	}

public:
	int age;
};


void test01()
{
	// 1、括号法
	Person p; // 默认构造函数调用(无参)
	Person p2(10); // 有参构造函数调用
	Person p3(p2); // 拷贝构造函数调用
	// 注意事项
	// 调用默认构造函数时候,不要加()
	// 因为这行代码:< Person p() >编译器会认为是一个函数声明
	
	// 2、显示法
	Person p11;
	Person p22 = Person(10); // 有参
	Person p33 = Person(p22); // 拷贝
	// Person(10)匿名对象 特点:当前行执行后,系统会立刻收回匿名对象
	// 注意事项
	// 不要利用拷贝函数 初始化匿名对象,编译器会认为Person(p3) == Person p3
	
	// 3、隐式转换法
	Person p4 = 10; // 有参
	Person p5 = p4; // 拷贝
}


int main()
{
	test01();
	system("pause");
	return 0;
}
类和对象—对象特性—拷贝函数调用时机

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

  • 使用一个已经创建完毕的对象来初始化一个新的对象
  • 值传递的方式给函数传值
  • 以值方式返回局部对象
#include<iostream>
using namespace std;


// 拷贝构造函数调用时机
class Person
{
public:
	Person()
	{
		cout << "这是默认构造函数调用" << endl;
	}

	Person(int a)
	{
		age = a;
		cout << "这是有参构造函数调用" << endl;
	}

	Person(const Person &p)
	{
		age = p.age;
		cout << "这是拷贝构造函数调用" << endl;
	}

	~Person()
	{
		cout << "这是析构函数调用" << endl;
	}

	int age;
};

// 1、使用已经创建完毕的对象来初始化一个新的对象
void test01()
{
	Person p1(10);
	Person p2(p1);
}

// 2、值传递的方式给函数参数传值

void Dowork(Person p)
{

}
void test02()
{
	Person p;
	Dowork(p);
}

// 3、值方式返回局部对象
Person dowork()
{
	Person p1;
	cout << (int*) & p1 << endl;
	return p1;
}

void test03()
{
	Person p = dowork();
	cout << (int*)&p<< endl;
}

int main()
{
	test01();
	test02();
	test03();
	system("pause");
	return 0;
}

这里显示并没有调用拷贝函数,弹幕说是编译器做了优化……,不过感觉无伤大雅。

类和对象—对象特性—构造函数调用规则

构造函数调用规则如下:

如果用户自定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造;

如果用户自定义拷贝构造函数,C++不会再提供其他构造函数。

#include <iostream>
using namespace std;

// 构造函数的调用规则
// 1、创建一个类编译器会提供至少三个函数,默认构造、析构函数、拷贝构造(值传递)
// 创建一个Person类
class Person
{
public:
	Person()
	{
		cout << "这是默认构造函数。" << endl;
	}

	Person(int age)
	{
		m_age = age;
		cout << "这是有参构造函数。" << endl;
	}

	//Person(const Person &p)
	//{
	//	cout << "这是拷贝构造函数。" << endl;
	//	m_age=p.m_age;
	//}

	~Person()
	{
		cout << "这是析构函数。" << endl;
	}

	int m_age;
};

void test01()
{
	Person p;
	p.m_age = 18;
	Person p2(p); //这里调用系统自带的拷贝函数
	cout << p2.m_age << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}
#include <iostream>
using namespace std;

// 构造函数的调用规则
// 1、创建一个类编译器会提供至少三个函数,默认构造、析构函数、拷贝构造(值传递)
// 创建一个Person类
class Person
{
public:
	//Person()
	//{
	//	cout << "这是默认构造函数。" << endl;
	//}

	Person(int age)
	{
		m_age = age;
		cout << "这是有参构造函数。" << endl;
	}

	//Person(const Person &p)
	//{
	//	cout << "这是拷贝构造函数。" << endl;
	//	m_age=p.m_age;
	//}

	~Person()
	{
		cout << "这是析构函数。" << endl;
	}

	int m_age;
};

//void test01()
//{
//	Person p;
//	p.m_age = 18;
//	Person p2(p);
//	cout << p2.m_age << endl;
//}

void test02()
{
	//Person p; 无法调用,系统不再提供默认构造
	Person p(18);
	Person p2(p); // 系统继续提供拷贝构造函数

}

int main()
{
	// test01();
	test02();
	system("pause");
	return 0;
}
#include <iostream>
using namespace std;

// 构造函数的调用规则
// 1、创建一个类编译器会提供至少三个函数,默认构造、析构函数、拷贝构造(值传递)
// 创建一个Person类
class Person
{
public:
	//Person()
	//{
	//	cout << "这是默认构造函数。" << endl;
	//}

	/*Person(int age)
	{
		m_age = age;
		cout << "这是有参构造函数。" << endl;
	}*/

	Person(const Person &p)
	{
		cout << "这是拷贝构造函数。" << endl;
		m_age=p.m_age;
	}

	~Person()
	{
		cout << "这是析构函数。" << endl;
	}

	int m_age;
};

//void test01()
//{
//	Person p;
//	p.m_age = 18;
//	Person p2(p);
//	cout << p2.m_age << endl;
//}


//2. 如果用户自定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造;
//void test02()
//{
//	//Person p; 无法调用,系统不再提供默认构造
//	Person p(18);
//	Person p2(p);
//
//}

// 3. 如果用户自定义拷贝构造函数,C++不会再提供其他构造函数。
void test03()
{
	// Person p; 无法调用,系统不再提供默认构造和有参构造
	// Person p2(18);
}

int main()
{
	// test01();
	//test02();
	system("pause");
	return 0;
}
类和对象—对象特性—深拷贝与浅拷贝

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

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

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

#include <iostream>
using namespace std;

class Person
{
public:

	Person(int age,int height)
	{
		m_Height = new int(height); // 把数据开辟到堆区
		m_age = age;
		cout << "这是有参构造函数。" << endl;
	}

	Person(const Person& p)
	{
		cout << "这是拷贝构造函数。" << endl;
		m_age = p.m_age;
		// m_Height = p.m_Height;系统提供的拷贝构造实现的就是这行代码
		//深拷贝
		m_Height = new int(*p.m_Height);
	}

	~Person()
	{
		// 将堆区的数据释放
		if (m_Height != NULL)
		{
			delete m_Height;
			m_Height = NULL; //防止野指针
		}
		cout << "这是析构函数。" << endl;
	}

	int m_age;
	int* m_Height;
};


void test01()
{
	Person p1(18, 160);
	cout << "p1的年龄是:" << p1.m_age <<"p1的身高是:"<< *p1.m_Height << endl;

	Person p2(p1);
	cout << "p2的年龄是:" << p2.m_age <<"p2的身高是:"<< *p2.m_Height << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}
类和对象—对象特性—初始化列表

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

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

#include<iostream>
using namespace std;

class Person
{
public:
	int m_a;
	int m_b;
	int m_c;
	// 初始化列表
	Person(int a,int b, int c) :m_a(a), m_b(b), m_c(c)
	{

	}
};

int main()
{
	Person p(10, 20, 30);
	cout << p.m_a<<" " << p.m_b <<" " << p.m_c << endl;  // 10 20 30
	cout<<
	system("pause");
	return 0;
}
类和对象—对象特性—类对象作为类成员

C++中允许一个类实例化的对象可以是另一个类的成员,我们称该成员为对象成员

#include<iostream>
using namespace std;
#include<string>

class Phone
{
public:
	string m_pname;


	Phone(string name)
	{
		cout << "Phone的构造函数调用。" << endl;
		m_pname = name;
	}

	~Phone()
	{
		cout << "Phone的析构函数调用。" << endl;
	}
};

class Person
{
public:
	string m_name;
	Phone m_phone;


	// Phone m_phone = pname; 隐式转换法
	Person(string name, string pname) :m_name(name), m_phone(pname)
	{
		cout << "Person的构造函数调用。" << endl;
	}

	~Person()
	{
		cout << "Person的析构函数调用。" << endl;
	}
};

//构造时候先构造其他类的对象,再构造自身。析构的顺序与构造相反。
void test011()
{
	Person p("张三", "苹果手机");
	cout << p.m_name << "拿着:" << p.m_phone.m_pname << endl;
}

int main()
{
	test011();
	system("pause");
	return 0;
}
C++类和对象—静态成员

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

  • 静态成员变量:

    所有对象共享同一份数据

    在编译阶段分配内存

    类内声明,类外初始化

    #include<iostream>
    using namespace std;
    
    class person
    {
    public:
    	static int m_a;
    	// 静态成员变量也是有访问权限的
    private:
    	static int m_b;
    };
    
    //类外初始化
    int person::m_a = 100;
    int person::m_b = 200;
    
    void test0111()
    {
    	person p;
    	cout << p.m_a << endl; // 100
    
    	person p2;
    	p2.m_a = 200;
    	cout << p2.m_a << endl; // 200
    }
    
    void test0222()
    {
    	//静态成员变量不属于某个对象上,所有对象都共享一份数据
    	//因此静态成员变量有两种访问方式
    	//1、通过对象进行访问
    	person p3;
    	cout << p3.m_a << endl;
    	//2、通过类名进行访问
    	cout << person::m_a << endl;
    	// cout << person::m_b << endl; 错误不可访问m_b
    }
    
    int main()
    {
    	//test0111();
    	test0222();
    
    	system("pause");
    	return 0;
    }
    

静态成员函数:

  • 所有对象共享同一个函数

  • 静态成员函数只能访问静态成员变量

    #include<iostream>
    using namespace std;
    
    class person
    {
    public:
    	static void func()
    	{
    		m_a = 100; //静态成员函数可以访问静态成员变量
    		// 静态成员函数不可以访问非静态成员变量,无法区分是哪个对象的m_b
    		// m_b = 30; 错误,不可访问
    
    		cout << "static void func 调用" << endl;
    	}
    	static int m_a;
    	int m_b;
    	// 静态成员函数也具有访问权限
    private:
    	static void func2()
    	{
    
    	}
    };
    
    //类外初始化
    int person::m_a = 0;
    
    void test0111()
    {
    	// 1、通过对象访问
    	person p;
    	p.func();
    
    	// 2、通过类名访问
    	person::func();
    	// person::func2(); 错误的,不可访问func2
    }
    
    
    int main()
    {
    	test0111();
    	system("pause");
    	return 0;
    }
    
类和对象—对象特性—成员变量和成员函数分开存储

只有非静态成员变量才属于类的对象上

#include<iostream>
using namespace std;

class APerson
{
	int m_A; //非静态成员变量属于类的对象上
	static int m_B; // 不属于类的对象上,不增加内存

	void func() // 非静态成员函数,不属于类的对象上
	{

	}

	static void func2() //静态成员函数,不属于类的对象上
	{

	}

};

int APerson::m_B = 10;

void test()
{
	APerson p;
	// C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
	// 每个空对象也应该有一个独一无二的内存地址
	// 未添加属性时,即空对象所占内存是1
	cout << "size of p = " << sizeof(p) << endl; // 4
}

int main()
{
	test();
	system("pause");
	return 0;
}
类和对象—对象特性—this指针的用途

C++this指针指向被调用的成员函数所属的对象。隐含在每个非静态函数内,无须定义,直接使用即可。

用途:

当形参和成员变量同名时,可以用this指针来区分。

在类的非静态成员函数中返回对象本身,可使用return \*this

#include<iostream>
using namespace std;


class Person
{
public:
	int age;

	Person(int age)
	{
		// this指针指向被调用的成员函数所属的对象
		this->age = age; // 属性和形参名冲突,如果不加this,系统无法识别age属性
	}

	void Addage(Person& p)
	{
		this->age += p.age;
	}

	Person& personAddage(Person& p)
	{
		this->age += p.age;
		return *this;
	}

};


// 1、解决名称冲突
void testa()
{
	Person p(18);
	cout << p.age << endl; // 18
}

// 2、返回对象本身用*this
void testb()
{
	Person p1(10);
	Person p2(20);
	p2.Addage(p1);
	cout << p2.age << endl; // 30
	// 链式编程思想
	p2.personAddage(p1).personAddage(p1).personAddage(p1);
	cout << p2.age << endl; // 60
}

int main()
{
	testa();
	testb();
	system("pause");
	return 0;
}
类和对象—对象特性—空指针访问成员函数

C++中允许空指针调用成员函数,但是也要注意有没有用到this指针,如果用到this指针,需要加以判断保证代码的健壮性。

#include<iostream>
using namespace std;


class Person
{
public:
	void showClassname()
	{
		cout << "this is Person class. " << endl;
	}
	void showPersonage()
	{
		// 报错原因是因为传入的指针是NULL。m_age无法用一个空的指向的this得到
		if (this == NULL)
		{
			return;
		}
		cout << "perspn age = " << m_age << endl;
	}

	int m_age;

};


void testa()
{
	Person* p = NULL;
	p->showClassname(); //可以运行
	// p->showPersonage(); 不能运行


}


int main()
{
	testa();
	system("pause");
	return 0;
}
类和对象—对象特性—const修饰成员函数(常函数)

常函数:

  • 成员函数后加const后我们称这个函数为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数
#include<iostream>
using namespace std;


class Person
{
public:
	
	// this指针本质上是指针常量,不可以修改它的指向
	// const Person *const this;
	// 成员函数后的const本质上是修饰this,让其指向的值也不能再修改
	void showPersonage() const
	{
		// m_age = 100; 错误的,不可以修改
		m_b = 10;
	}
	void func()
	{

	}

	int m_age;
	mutable int m_b;

};


void testa()
{
	Person p;
	p.showPersonage();
	cout << p.m_b << endl; // 10

}

void testb()
{
	const Person p;
	// p.m_age = 10; 错误不可修改
	p.m_b = 20; // 在常对象也可以修改
	// 常对象只能调用常函数
	p.showPersonage();
	// p.func(); 常对象不可以调用普通成员函数,因为普通成员函数可以修改属性,但是常对象不允许修改属性


}
int main()
{
	testa();
	system("pause");
	return 0;
}

C++类和对象—友元

类和对象—友元—全局函数做友元

友元的目的就是让一个函数或者类访问另一个类中私有成员,关键字是friend

友元的三种实现:

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元
#include<iostream>
using namespace std;
#include<string>

class Building
{
	// 现在visiting全局函数是Building的友元可以访问Building中的私有成员了
	friend void visting(Building* building);
public:
	Building()
	{
		m_SettingRoom = "客厅";
		m_BedRoom = "卧室";
	}
public:
	string m_SettingRoom;
private:
	string m_BedRoom;
};

//
void visting(Building *building)
{
	cout << "正在访问:" << building->m_SettingRoom << endl;
	cout << "正在访问:" << building->m_BedRoom << endl;
}

void test01()
{
	Building building;
	visting(&building);
}
int main()
{
	test01();
	system("pause");
	return 0;
}
类和对象—友元—友元类

类做友元。

#include<iostream>
using namespace std;
#include<string>

class Building
{
	friend class Friend; // Friend 可以访问Building的私有成员
public:
	Building();
public:
	string m_SettingRoom;
private:
	string m_BedRoom;
};

class Friend
{
public:
	void visit(); // 参观函数访问building中的属性
	Friend();
	Building* building;
};


// 在类外写函数
Building::Building()
{
	m_SettingRoom = "客厅";
	m_BedRoom = "卧室";
}
Friend::Friend()
{
	// 创建building对象
	building = new Building;
}

void Friend::visit()
{
	cout << "Friend正在访问:" << building->m_SettingRoom << endl;
	cout << "Friend正在访问:" << building->m_BedRoom << endl;
}

void test01()
{
	Friend ff;
	ff.visit();
}

int main()
{
	test01();
	system("pause");
	return 0;
}
类和对象—友元—成员函数做友元
#include<iostream>
using namespace std;
#include<string>

class Building; //提前声明,因为Friend类中要创建Building类的指针

class Friend
{
public:
	Friend();
	Building* building;
	void visit(); // 让其做友元函数
	void visit2(); // 普通函数
};

class Building 
{
	friend void Friend::visit(); // visit函数必须在前面声明,因为这里再次声明其为友元函数
public:
	Building();
public:
	string m_SettingRoom;
private:
	string m_BedRoom;
};


Building::Building()
{
	m_BedRoom = "卧室";
	m_SettingRoom = "客厅";
}

Friend::Friend()
{
	building = new Building;
}

void Friend::visit()
{
	cout << "Friend正在访问: " << building->m_SettingRoom << endl;
	cout << "Friend正在访问: " << building->m_BedRoom << endl;
}

void Friend::visit2()
{
	cout << "Friend正在访问: " << building->m_SettingRoom << endl;
	// cout << "Friend正在访问: " << building->m_BedRoom << endl;错误的visit02()不可以访问
}

void test01()
{
	Friend ff;
	ff.visit();
}

int main()
{
	test01();
	system("pause");
	return 0;
}

C++类和对象—C++运算符重载

类和对象—C++运算符重载—加号运算符重载

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

#include<iostream>
using namespace std;
//加号运算符重载
class Person
{
public:
	// 1、成员函数重载
	Person operator+(Person& p)
	{
		Person tmp;
		tmp.m_A = this->m_A + p.m_A;
		tmp.m_B = this->m_B + p.m_B;
		return tmp;
	}
	int m_A;
	int m_B;

};

//2、全局函数重载
Person operator+(Person& p1, Person& p2)
{
	Person tmp;
	tmp.m_A = p1.m_A + p2.m_A;
	tmp.m_B = p1.m_B + p2.m_B;
	return tmp;
}
// 运算符重载也可以发生函数重载
Person operator+(Person& p1, int num)
{
	Person tmp;
	tmp.m_A = p1.m_A + num;
	tmp.m_B = p1.m_B + num;
	return tmp;
}

void test01()
{
	Person p1;
	p1.m_A = 10;
	p1.m_B = 20;
	Person p2;
	p2.m_A = 10;
	p2.m_B = 20;
	Person p3 = p1 + p2;
	Person p4 = p1 + 100;

	cout << "p3.m_A= " << p3.m_A << " p3.m_B= " << p3.m_B << endl;
	cout << "p4.m_A= " << p4.m_A << " p4.m_B= " << p4.m_B << endl;
}


int  main()
{
	test01();
	system("pause");
	return 0;
}
类和对象—C++运算符重载—左移运算符重载

作用:可以输出自定义数据类型。

#include<iostream>
using namespace std;

class Person
{
	friend ostream& operator<<(ostream& cout, Person& p);
public:
	Person(int a, int b)
	{
		m_A = a;
		m_B = b;
	}
public:
	int m_A;
private:
	int m_B;

	//1、利用成员函数重载左移运算符
	// void operator<<(cout)
	// 无法得到cout<<形式的输出结果,因此只能利用全局函数重载左移运算符
	
};

// 2、利用全局函数重载左移运算符
ostream & operator<<(ostream &cout, Person &p)
{
	cout << "p.m_A= " << p.m_A << " p.m_B= " << p.m_B;
	return cout; // out属于ostream输出流数据类型
}

void test01()
{
	Person p(20, 10);
	cout << p << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}
类和对象—C++运算符重载—递增运算符重载

作用:通过重载递增运算符,实现自己的整型数据

#include<iostream>
using namespace std;
// ++运算符重载
class MyInteger
{
	friend ostream& operator<<(ostream &cout, MyInteger myint);
public:
	MyInteger()
	{
		m_Num = 0;
	}
	// 前置++
	MyInteger& operator++()  // 返回引用是为了对一个数据进行操作
	{
		//先进行++运算
		m_Num++;
		//再对自身进行返回
		return *this;
	}
	// 后置++
	//int 代表占位参数,可以用于区分前置和后置参数
	MyInteger operator++(int)  //后置递增是返回值
	{
		MyInteger tmp = *this;
		m_Num++;
		return tmp;
	}

private:
	int m_Num;
};

//左移运算符重载
ostream & operator<<(ostream &cout, MyInteger myint)
{
	cout << myint.m_Num;
	return cout;
}

void test01()
{
	MyInteger myint1;
	cout << ++myint1 << endl;
	cout << myint1 << endl;
	MyInteger myint2;
	cout << myint2++ << endl;
	cout << myint2 << endl;

}

int main()
{
	test01();
	system("pause");
	return 0;
}
类和对象—C++运算符重载—赋值运算符重载

C++编译器至少给一个类添加4个函数,默认构造函数、默认析构函数、默认拷贝构造函数、赋值运算符operator=,对属性进行值拷贝

如果类中有属性指向堆区,做赋值操作也会出现深浅拷贝问题。

#include <iostream>
using namespace std;

class Person
{
public:
	Person(int age)
	{
		m_Age = new int(age);
	}

	~Person()
	{
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
	}

	//重载赋值运算符
	Person& operator=(Person& p)
	{
		//这是编译器提供的赋值操作,编译器提供的赋值操作会出现浅拷贝问题
		// m_Age = p.m_Age;
		//应该先判断是否有属性在堆区,如果有先释放干净,然后再进行深拷贝
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
		//深拷贝
		m_Age = new int(*p.m_Age);
		//返回自身,这样才能实现连等操作
		return *this;
	}

	int *m_Age;
};


void test01()
{
	Person p(18);
	Person p2(20);
	Person p3(30);
	p = p2 = p3;
	cout << "p.age= " << *p.m_Age << endl;
	cout << "p2.age= " << *p2.m_Age << endl;
	cout << "p3.age= " << *p3.m_Age << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}
类和对象—C++运算符重载—关系运算符重载

作用:重载关系运算符,可以让两个自定义类型对象进行对比操作

#include<iostream>
using namespace std;

class Person
{
public:
	Person(string name, int age)
	{
		m_name = name;
		m_age = age;
	}

	bool operator==(Person& p)
	{
		if (this->m_name == p.m_name && this->m_age == p.m_age)
		{
			return true;
		}
		return false;
	}

	bool operator!=(Person& p)
	{
		if (this->m_name == p.m_name && this->m_age == p.m_age)
		{
			return false;
		}
		return true;
	}

	string m_name;
	int m_age;
};

void test01()
{
	Person p1("Tom", 19);
	Person p2("Tom", 18);
	if (p1 == p2)
	{
		cout << "p1 = p2" << endl;
	}
	else
	{
		cout << "p1 != p2" << endl;
	}
	if (p1 != p2)
	{
		cout << "p1 != p2" << endl;
	}
	else
	{
		cout << "p1 = p2" << endl;
	}
}

int main() 
{
	test01();
	system("pause");
	return 0;
}
类和对象—C++运算符重载—函数调用运算符重载

函数调用运算符()也可以重载

由于重载后使用的方式非常像函数调用,因此称为仿函数

仿函数没有固定写法,非常灵活

#include<iostream>
using namespace std;
#include<string>
class MyPrint
{
public:
	//函数调用运算符重载
	void operator()(string test)
	{
		cout << test << endl;
	}
};

class MyAdd
{
public:
	//函数调用运算符重载
	int operator()(int num1,int num2)
	{
		return num1 + num2;
	}
};
void test01()
{
	MyPrint myprint;
	myprint("hello cpp");
	MyAdd myadd;
	int ret = myadd(10, 20);
	cout << "ret= " << ret << endl;
	//匿名函数对象
	cout << MyAdd()(100, 200) << endl;


}

int main()
{
	test01();
	system("pause");
	return 0;
}

C++类和对象—继承

类和对象—继承—基本语法

继承是面向对象三大特性之一

定义一些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。

这个时候我们就可以考虑利用继承的技术,减少重复代码。

#include<iostream>
using namespace std;

//普通实现页面,会有很多重复代码
//JAVA页面

class Java
{
public:
	void header()
	{
		cout << "首页、公开课、登录、注册…(公共头部)" << endl;
	}

	void footer()
	{
		cout << "帮助中心、交流合作、站内地图…(公共底部)" << endl;
	}

	void lefter()
	{
		cout << "JAVA、python、C++…(公共分类列表)" << endl;
	}

	void content()
	{
		cout << "Java学科视频" << endl;
	}
};


class Python
{
public:
	void header()
	{
		cout << "首页、公开课、登录、注册…(公共头部)" << endl;
	}

	void footer()
	{
		cout << "帮助中心、交流合作、站内地图…(公共底部)" << endl;
	}

	void lefter()
	{
		cout << "JAVA、python、C++…(公共分类列表)" << endl;
	}

	void content()
	{
		cout << "Python学科视频" << endl;
	}
};

class Cpp
{
public:
	void header()
	{
		cout << "首页、公开课、登录、注册…(公共头部)" << endl;
	}

	void footer()
	{
		cout << "帮助中心、交流合作、站内地图…(公共底部)" << endl;
	}

	void lefter()
	{
		cout << "JAVA、python、C++…(公共分类列表)" << endl;
	}

	void content()
	{
		cout << "Cpp学科视频" << endl;
	}
};

void test01()
{
	cout << "下载java主页: " << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.lefter();
	ja.content();
	cout << "******************************" << endl;

	cout << "下载Python主页: " << endl;
	Python pn;
	pn.header();
	pn.footer();
	pn.lefter();
	pn.content();
	cout << "******************************" << endl;

	cout << "下载Cpp主页: " << endl;
	Cpp cp;
	cp.header();
	cp.footer();
	cp.lefter();
	cp.content();

}

int main()
{
	test01();
	system("pause");
	return 0;
}
#include<iostream>
using namespace std;

//继承类页面实现
//继承的好处:减少重复代码
//语法:class 子类:继承方式 父类
//子类也称为派生类
//父类也成为基类
class Basepage
{
public:

	void header()
	{
		cout << "首页、公开课、登录、注册…(公共头部)" << endl;
	}

	void footer()
	{
		cout << "帮助中心、交流合作、站内地图…(公共底部)" << endl;
	}

	void lefter()
	{
		cout << "java、python、c++…(公共分类列表)" << endl;
	}
};
//Jave
class Java:public Basepage
{
public:
	void content()
	{
		cout << "Java学科视频" << endl;
	}
};


//Python
class Python :public Basepage
{
public:
	void content()
	{
		cout << "Python学科视频" << endl;
	}
};


//C++
class Cpp :public Basepage
{
public:
	void content()
	{
		cout << "C++学科视频" << endl;
	}
};


void test01()
{
	cout << "下载java主页: " << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.lefter();
	ja.content();
	cout << "******************************" << endl;

	cout << "下载python主页: " << endl;
	Python pn;
	pn.header();
	pn.footer();
	pn.lefter();
	pn.content();
	cout << "******************************" << endl;

	cout << "下载cpp主页: " << endl;
	Cpp cp;
	cp.header();
	cp.footer();
	cp.lefter();
	cp.content();

}


int main()
{
	test01();
	system("pause");
	return 0;
}
类和对象—继承—继承方式

继承语法:class 子类:继承方式 父类

继承方式一共有三种:公共继承、保护继承、私有继承

#include<iostream>
using namespace std;

//继承方式
class Base
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;

};

class Son1 :public Base
{
public:
	void func()
	{
		m_A = 10; //父类中的公共权限成员,子类中依然是公共权限
		m_B = 20; //父类中的保护权限成员,子类中依然是保护权限
		// m_C = 10; 父类中的私有权限成员 子类访问不到
	}
};

//保护继承
void test01()
{
	Son1 s1;
	s1.m_A = 10;
	// s1.m_B = 20;到son1中,m_B是保护权限,类外访问不到
}

class Son2 :protected Base
{
public:
	void func()
	{
		m_A = 10; //父类中的公共权限成员,子类中为保护权限
		m_B = 20; //父类中的保护权限成员,子类中为保护权限
		// m_C = 10; 父类中的私有权限成员 子类访问不到
	}
};

void test02()
{
	Son2 s2;
	// s2.m_A = 10; 到son2中,m_A是保护权限,类外访问不到
	// s2.m_B = 20;到son2中,m_B是保护权限,类外访问不到
}

//私有继承
class Son3 :private Base
{
public:
	void func()
	{
		m_A = 10; //父类中的公共权限成员,子类中为私有权限
		m_B = 20; //父类中的保护权限成员,子类中为私有权限
		// m_C = 10; 父类中的私有权限成员 子类访问不到
	}
};

void test03()
{
	Son3 s3;
	// s3.m_A = 10; 到son3中,m_A是私有权限,类外访问不到
	// s3.m_B = 20;到son3中,m_B是私有权限,类外访问不到
}

class GrandSon3 :public Son3
{
public:
	void func()
	{
		// m_A = 10; //父类中的私有权限成员,子类访问不到
		// m_B = 20; //父类中的私有权限成员,子类访问不到
		// m_C = 10; 父类中的私有权限成员 子类访问不到
	}
};

int main()
{
	system("pause");
	return 0;
}
类和对象—继承—继承中的对象模型

问题:从父类继承过来的成员,哪些属于子类对象中?

#include <iostream>
using namespace std;

//
class Base
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son :public Base
{
public:
	int m_D;
};

void test01()
{
	//父类中的所有非静态成员属性都会被子类继承下去
	//父类中私有成员属性,是被编译器隐藏了,因此访问不到,但是确实被继承下去了
	cout << "size of Son: " << sizeof(Son) << endl; //16
}
int main()
{
	test01();
	system("pause");
	return 0;
}

利用开发人员命令提示工具查看对象模型;跳转盘符*D:*;跳转查看文件路径 *cd 具体路径*;查看你命名*dir**c1 \dl reportSingleClassLayout类名 文件名*

类和对象—继承—构造和析构顺序

问题:子类继承父类后,当创建子类对象,也会调用父类的构造函数,父类和子类的构造和析构顺序是谁先谁后?

#include<iostream>
using namespace std;

class Base
{
public:
	Base()
	{
		cout << "Base构造函数!" << endl;
	}

	~Base()
	{
		cout << "Base析构函数!" << endl;
	}
};

class Son :public Base
{
public:
	Son()
	{
		cout << "Son构造函数!" << endl;
	}

	~Son()
	{
		cout << "Son析构函数!" << endl;
	}
};
void test01()
{
    //继承中的构造和析构顺序如下;
    //先构造父类,再构造子类,析构的顺序与构造的顺序相反
	Son s;
}
int main()
{
	test01();
	system("pause");
	return 0;
}
类和对象—继承—同名成员处理

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

访问子类同名成员,直接访问即可

访问父类同名成员,需要加作用域

#include <iostream>
using namespace std;

class Base
{
public:
	int m_A;

	Base()
	{
		m_A = 100;
	}

	void func()
	{
		cout << "Base_func()" << endl;
	}

	void func(int a)
	{
		cout << "有参Base_func()" << endl;
	}
};

class Son :public Base
{
public:
	int m_A;

	Son()
	{
		m_A = 200;
	}

	void func()
	{
		cout << "Son_func()" << endl;
	}

};
// 同名成员属性
void test01()
{
	Son s;
	cout << s.m_A << endl; //200
	cout << s.Base::m_A << endl; //100


}

//同名成员函数
void test02()
{
	Son s;
	s.func(); //Son_func()
	s.Base::func();//Base_func()
	//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名函数
	// 如果想访问到父类中被隐藏的同名成员函数,需要加作用域
	// s.func(100); 错误,访问不到父类
	s.Base::func(100);
}

int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}
类和对象—继承—同名静态成员处理

问题:继承中同名的静态成员在子类对象上如何进行访问?

静态成员和非静态成员出现同名,处理方式一致。

访问子类同名成员,直接访问即可;

访问父类同名成员,需要加作用域。

#include<iostream>
using namespace std;

class Base
{
public:
	static int m_A;

	static void func()
	{
		cout << "Base static func()" << endl;
	}
};
int Base::m_A = 100;

class Son:public Base
{
public:
	static int m_A;

	static void func()
	{
		cout << "Son static func()" << endl;
	}
};
int Son::m_A = 200;

// 同名静态成员属性
void test01()
{
	//1、通过对象访问
	Son s;
	cout << s.m_A << endl; //200
	cout << s.Base::m_A << endl; //100
	//2、通过类名访问
	cout << Son::m_A << endl; //200
	//第一个::代表通过类名访问,第二个::代表访问父类作用域下
	cout << Son::Base::m_A << endl; //100 
}

// 同名静态成员函数
void test02()
{
	//1、通过对象访问
	Son s;
	s.func(); //Son static func()
	s.Base::func(); //Base static func()
	//2、通过类名访问
	Son::func(); //Son static func()
	Son::Base::func(); //Base static func()
}
int main()
{
	test01();
	test02();
	system("pause");
	return 0;
}

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

类和对象—继承—继承语法

C++允许一个类继承多个类

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

多继承可能会引起父类中有同名成员出现,需要加作用域区分

#include<iostream>
using namespace std;

class Base1
{
public:
	int m_A;
	Base1()
	{
		m_A = 100;
	}
};

class Base2
{
public:
	int m_A;
	Base2()
	{
		m_A = 200;
	}
};

//子类,需要继承两个父类
class Son :public Base1, public Base2
{
public:
	Son()
	{
		m_C = 300;
		m_D = 400;
	}
	int m_C;
	int m_D;
};

void test01()
{
	Son s;
	cout << "sizeof Son = " << sizeof(s) << endl; //16
	cout << "m_A= " << s.Base1::m_A << endl; //同名要加作用域
	cout << "m_A= " << s.Base2::m_A << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}
类和对象—继承—菱形继承问题以及解决方法

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

遇到的问题:二义性;同样属性的数据没必要开辟两个内存空间。

#include<iostream>
using namespace std;

class Animal 
{
public:
	int m_age;

};
// 利用虚继承可以解决菱形继承的问题

class Sheep:virtual public Animal{};

class Tuo:virtual public Animal{};

class SheepTuo:public Sheep,public Tuo{};

void test01()
{
	SheepTuo st;
	// st.m_age=18;错误不明确
	st.Sheep::m_age = 18;
	st.Tuo::m_age = 10;
	cout << "st.Sheep::m_age= " << st.Sheep::m_age << endl;
	cout << "st.Tuo::m_age= " << st.Tuo::m_age << endl; 
	//这份数据只要一份就行,菱形继承导致了资源浪费
	//加了virtual后m_age成为共享的内存

}

int main()
{
	test01();
	system("pause");
	return 0;
}

C++类和对象—多态

类和对象—多态—多态的基本语法

多态分为静态多态(函数重载和运算符重载)和动态多态(派生类和虚函数实现运行时多态),二者区别:

  • 静态多态的函数地址早绑定—编译阶段确定函数地址
  • 动态多态的函数地址晚绑定—运行阶段确定函数地址
#include <iostream>
using namespace std;

class Animal {
public:
    // 虚函数
    virtual void speak() {
        cout << "Animal speaking" << endl;
    }
};

class Cat :public Animal {
public:
    void speak() {
        cout << "Cat speaking" << endl;
    }
};

// 地址早绑定,在编译阶段确定函数地址
// 动态多态满足条件
// 1、继承关系
// 2、子类要重写(函数返回值、函数名、参数列表完全相同)父类中的虚函数
// 父类的指针指向子类的对象
void Dospeak(Animal& animal) {
    animal.speak();
}

void test01() {
    Cat cat;
    Dospeak(cat);
}


int main()
{
    test01();
    system("pause");
    return 0;
}
类和对象—多态—多态的原理剖析

在这里插入图片描述

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值