目录
Hello world
#include<iostream>
using namespace std;
int main()
{
cout<<"hello world"<<endl;
system("pause");
return 0;
}
\\endl 为换行符
变量
变量创建的语法 :数据类型 变量名 = 变量初始值;
#include<iostream>
using namespace std;
int main()
{
int a = 10;
cout<<"a = "<<a<<endl;
system("pasue");
return 0;
}
常量
#define 宏常量:#define 常量名 常量值
通常在文件上方定义,表示一个常量
const 修饰变量 const 数据类型 常量名 = 常量值;
通常在变量定义前加关键字const,修饰该变量为常量,不可修改
#include<iostream>
using namespace std;
#define Day 7
int main()
{
cout<<"一周有"<< Day <<"天"<<endl;
system("pause");
return 0;
}
#include<iostream>
using namespace std;
int main()
{
const int a = 12;
cout<<a<<endl;
system("pause");
return 0;
}
\\变量a 不可修改
关键字
创建变量时,取名不可使用关键字
标识符命名规则
- 不可谓关键字
- 只能为字母、数字、下划线
- 第一个字符只能为字母或者下划线
- 标识符中区分大小写
sizeof关键字
可以利用sizeof求出数据类型占用内存大小
语法:sizeof(数据类型/变量)
#include<iostream>
using namespace std;
int main()
{
short num1 = 12;
cout<<"数据类型占用空间大小"<<sizeof(num1)<<endl;
system("pause");
return 0;
}
浮点型(实型)
单精度float(有效数字 7位)
双精度double(有效数字 15-16位)
#include<iostream>
using namespace std;
int main()
{
//默认情况下,输出一个小数,会显示出六位有效数字
float f1 = 3.14f;
cout<<"f1="<<f1<<endl;
double d1 = 3.14d;
cout<<"d1="<<d1<<endl;
//统计float和double占用内存空间
cout<<"float占用内存空间为:"<<sizeof(float)<<endl;
cout<<"double占用内存空间为:"<<sizeof(double)<<endl;
//科学计数法
float f2 = 3e2; //如果e后面是一个正数,则代表“3 * 10^2 ”
cout<<"f2="<<f2<<endl;
float f3 = 3e-2; //如果e后面是一个负数,则代表“3 * 0.1^2 ”
cout<<"f3="<<f3<<endl;
system("pause");
return 0;
}
字符型
注:1、在显示字符型变量时,用单引号将字符括起来,不要用双引号
2、单引号内只能有一个字符,不可以是字符串
3、C和C++里字符只占用1个字节
4、字符型变量不是把字符本身放到内存中存储,而是将对应的ASCII编码放入存储单元中
5、ASCII表上0-31为控制字符,32-126键盘上能找到
#include<iostream>
using namespace std;
int main()
{
//字符型变量的创建方式
char ch = 'a';
cout<<ch<<endl;
//字符型变量所占存储空间大小
cout<<sizeof(ch)<<endl;
//字符型变量常见错误
//char ch2 = "b"; 创建字符型变量时,要用单引号
//char ch2 = 'abc'; 创建字符型变量时,单引号内只能有一个字符
//字符型变量对应ASCII编码 常用: a = 97 A = 65
cout<<(int)ch<<endl; //(int)ch 指将ch强制转换成整型
system("pause");
}
转义字符
#include<iostream>
using namespace std;
int main()
{
//转义字符
//换行符 \n
cout<<"hello world"<<endl;
//反斜杠 \\ 第一个\表示接下来输出的是特殊字符
cout<<"\\"<<endl;
//水平制表符 \t 作用:整齐的输出数据
cout<<"aaa\thello world"<<endl;
cout<<"aaaa\thello world"<<endl;
cout<<"aaaaa\thello world"<<endl;
system("pause");
}
字符串型
1、c语言风格:char 变量名[] = "字符串值";
注意:char 字符串名 []
等号后面要用双引号包含起来字符串
#include<iostream>
using namespace std;
int main()
{
char str[] = "hello world";
cout<<str<<endl;
return 0;
}
2、c++风格:string str1 = "hello world";
注意:要包含一个头文件 #include<string>
#include<iostream>
using namespace std;
#include<string>//头文件
int main()
{
string str1 = "hello world";
cout<<str1<<endl;
system("pause");
}
布尔类型bool
作用:表示真假的值
#include<iostream>
using namespace std;
int main()
{
//创建bool数据类型
bool flag = true; //true代表真
cout<<flag<<endl;
flag = false; //false代表假
cout<<flag<<endl;
//本质上:1代表真 2代表假
//查看bool类型所占内存
cout<<sizeof(bool)<<endl;
system("pause");
}
数据的输入
cin>>变量
整型:
#include<iostream>
using namespace std;
int main()
{
int a = 0;
cout<<"请给整型变量a赋值"<<endl;
cin>>a;
cout<<"整型变量a = "<<a<<endl;
system("pause");
}
布尔类型:
#include<iostream>
using namespace std;
int main()
{
bool a = false;
cout<<"请给布尔类型a赋值"<<endl;
cin>>a;
cout<<"布尔类型a = "<<a<<endl;
system("pause");
} //除了0以外都是真,输出1
运算符
算术运算符
注:两个整数相除,结果依然是整数,将小数部分除去
两数相除,除数不可以为零
两个小数不可以做取模(余数)运算
#include<iostream>
using namespace std;
int main()
{
//前置递增
int a = 10;
++a; //让变量+1
cout<<"a="<<a<<endl;
//后置递增
int b = 10;
b++; //让变量+1
cout<<"b="<<b<<endl;
//前置与后置的区别
//前置递增:先让变量+1,然后进行表达式运算
int a2 = 10;
int b2 = ++a2 * 10;
cout<<"a2="<<a2<<endl;
cout<<"b2="<<b2<<endl;
//后置递增:先进行表达式运算,后让变量+1
int a3 = 10;
int b3 = a3++ * 10; //先将值代入,运算结束赋予b3后,a3再进行+1
cout<<"a3="<<a3<<endl;
cout<<"b3="<<b3<<endl;
system("pause");
}
赋值运算符
a + = 2 //表示 a = a + 2
逻辑运算符
程序流程结构
选择结构
If语句
单行格式if语句
#include<iostream>
using namespace std;
int main()
{
//选择结构 单行 if从句
//用户输入分数,若分数大于 600,视为考上一本大学,并输出
//用户输入分数
int score = 0;
cout<<"请输入一个分数"<<endl;
cin>>score;
//打印用户输入的分数
cout<<"您输入的分数为:"<<score<<endl;
//判断分数是否大于600,若大于,输出
if(score>600)
{
cout<<"恭喜您考上了一本大学"<<endl;
}
system("pause");
return 0;
}
注:if后不加 分号
多行格式if语句
#include<iostream>
using namespace std;
int main()
{
//选择结构 - 多行if语句
//输入考试分数,若分数大于600,视为考上一本大学,在屏幕上输出,若没考上,打印未考上一本大学
//输入考试成绩
int score = 0;
cout<<"请输入考试成绩"<<endl;
cin>>score;
//提示用户输入的分数
cout<<"您输入的分数为"<<score<<endl;
//判断 若大于600,打印考上一本,否则打印未考上一本
if(score>600)
{
cout<<"恭喜考上一本大学"<<endl;
}
else
{
cout<<"名落孙山"<<endl;
}
system("pause");
return 0;
}
多条件的if语句
if条件(条件1),else if(条件2),else(条件3)…
#include<iostream>
using namespace std;
int main()
{
//选择结构 - 多条件if语句
//输入考试分数,若分数大于600,视为考上一本大学,在屏幕上输出,若没考上,打印未考上一本大学
//大于500分,视为考上二本大学,在屏幕上输出
//大于400分,视为考上三本大学,在屏幕上输出
//小于等于400,视为未考上大学,在屏幕上输出
//输入考试成绩
int score = 0;
cout<<"请输入考试成绩"<<endl;
cin>>score;
//提示用户输入的分数
cout<<"您输入的分数为"<<score<<endl;
//判断 若大于600,打印考上一本
//若大于500,打印考上二本
//若大于400,打印考上三本
//都不满足,打印名落孙山
if(score>600)
{
cout<<"恭喜考上一本大学"<<endl;
}
else if(score>500)
{
cout<<"恭喜考上二本大学"<<endl;
}
else if(score>400)
{
cout<<"恭喜考上三本大学"<<endl;
}
else
{
cout<<"名落孙山"<<endl;
}
system("pause");
return 0;
}
嵌套if语句
#include<iostream>
using namespace std;
int main()
{
//选择结构 - 嵌套if语句
//输入考试分数,若分数大于500,视为考上一本大学,在屏幕上输出,若没考上,打印未考上一本大学
//大于400分,视为考上二本大学,在屏幕上输出
//大于300分,视为考上三本大学,在屏幕上输出
//小于等于300,视为未考上大学,在屏幕上输出
//在一本分数中,大于700分,考入北大,大于650分,考入清华,大于600分,考入人大
//输入考试成绩
int score = 0;
cout<<"请输入考试成绩"<<endl;
cin>>score;
//提示用户输入的分数
cout<<"您输入的分数为"<<score<<endl;
//判断 若大于500,打印考上一本
//若大于400,打印考上二本
//若大于300,打印考上三本
//都不满足,打印名落孙山
if(score>500)
{
cout<<"恭喜考上一本大学"<<endl;
if(score>700)
{
cout<<"恭喜考入北大"<<endl;
}
else if(score>650)
{
cout<<"恭喜考入清华"<<endl;
}
else if(score>600)
{
cout<<"恭喜考入人大"<<endl;
}
}
else if(score>500)
{
cout<<"恭喜考上二本大学"<<endl;
}
else if(score>400)
{
cout<<"恭喜考上三本大学"<<endl;
}
else
{
cout<<"名落孙山"<<endl;
}
system("pause");
return 0;
}
练习案例:三只系小猪称体重
有三只小猪ABC,分别输入三只小猪的体重,并判断哪只小猪最重
#include<iostream>
using namespace std;
int main()
{
//先判断A和B谁重,在与C比较
int a = 0;
int b = 0;
int c = 0;
cout<<"请输入小猪A的重量"<<endl;
cin>>a;
cout<<"请输入小猪B的重量"<<endl;
cin>>b;
cout<<"请输入小猪C的重量"<<endl;
cin>>c;
cout<<"小猪A的重量为:"<<a<<endl;
cout<<"小猪B的重量为:"<<b<<endl;
cout<<"小猪C的重量为:"<<c<<endl;
if(a>b)
{
if(a>c)
{
cout<<"小猪A为最重的:"<<a<<endl;
}
else if(a<c)
{
cout<<"小猪C为最重的:"<<c<<endl;
}
}
else if(a<b)
{
if(b>c)
{
cout<<"小猪B为最重的:"<<b<<endl;
}
else if(b<c)
{
cout<<"小猪C为最重的:"<<c<<endl;
}
}
system("pause");
return 0;
}
三目运算符
语法:表达式1 ? 表达式2 :表达式3
解释:若表达式1的值为真,执行表达式2,并返回表达式2的结果
若表达式1的值为假,执行表达式3,并返回表达式3的结果
#include<iostream>
using namespace std;
int main()
{
//三目运算符
//创建三个变量
//将a和b作比较,将变量大的值赋值给c
int a = 10;
int b = 20;
int c = 0;
c = (a>b ? a:b);
cout<<c<<endl;
//三目运算符返回的是变量,可以继续赋值
(a<b ? a:b) = 100;
cout<<a<<endl;
cout<<b<<endl;
system("pause");
return 0;
}
Switch语句(执行多条件分支语句)
switch case break default(相当于else)
#include<iostream>
using namespace std;
int main()
{
//switch 语句
//给电影打分
//10-9 经典
//8-7 非常好
//6-5 一般
//5以下 烂片
//提示用户给电影打分
cout<<"请给电影进行打分"<<endl;
//用户进行打分
int score = 0;
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;
case 5:
cout<<"一般"<<endl;
break;
default:
cout<<"烂片"<<endl;
break;
}
//if 和 switch 的区别
//switch 缺点:判断分支时,只能是整型或者字符型,不可以是区间
//switch 优点:结构清晰,执行效率高
system("pause");
return 0;
}
循环结构
while循环
While(循环条件)(循环语句)
#include<iostream>
using namespace std;
int main()
{
//while 循环
//屏幕上打印0~9十个数
int num = 0;
while(num<10)
{
cout<<num<<endl;
num++;
}
system("pause");
return 0;
}
案例练习
系统随机生成一个1-100之间的数字,玩家进行猜测,若猜错,提示玩家数字过大或过小,若猜对,恭喜玩家胜利,结束游戏。
#include<iostream>
#include<cstdlib> // rand()和srand() 包含在这个库中
#include<ctime> //time 系统时间头文件
using namespace std;
int main()
{
cout << "\t----------" << endl; // \t 为水平制表符
cout << "\t|猜数游戏|(1-100)" << endl;
cout << "\t----------" << endl;
cout<<"请玩家进行猜测"<<endl;
//添加随机数种子,作用:利用系统当前的时间生成随机数,防止每次随机数都一样
srand((unsigned int)time(NULL));
//系统生成一个随机数
int num1 = rand() % 100+1; //rand()%100 生成0-99的随机数
//玩家进行猜测
int num2 = 0; //玩家输入的数据
int a = 0; //统计猜测次数
while(1)
{
cin>>num2;
//判断玩家的猜测
if(num2>num1)
{
cout<<"猜测过大"<<endl;
a++;
}
else if(num2<num1)
{
cout<<"猜测过小"<<endl;
a++;
}
else
{
cout<<"恭喜猜对了"<<endl;
a++;
break;
}
//猜对 退出游戏
//猜错 提示猜的结果,返回第二步
if(a == 7)
{
cout<<"猜测次数过多,你输了!"<<endl;
}
}
system("pause");
return 0;
}
do...while循环语句
do(循环语句) while(循环条件)
注:与while的区别在于do…while会先进行一次循环语句,再判断循环条件
while是先判断循环条件,再执行循环语句
#include<iostream>
#include<cstdlib> // rand()和srand() 包含在这个库中
#include<ctime> //time 系统时间头文件
using namespace std;
int main()
{
cout << "\t----------" << endl; // \t 为水平制表符
cout << "\t|猜数游戏|(1-100)" << endl;
cout << "\t----------" << endl;
cout<<"请玩家进行猜测"<<endl;
//添加随机数种子,作用:利用系统当前的时间生成随机数,防止每次随机数都一样
srand((unsigned int)time(NULL));
//系统生成一个随机数
int num1 = rand() % 100+1; //rand()%100 生成0-99的随机数
//玩家进行猜测
int num2 = 0; //玩家输入的数据
int a = 0; //统计猜测次数
while(1)
{
cin>>num2;
//判断玩家的猜测
if(num2>num1)
{
cout<<"猜测过大"<<endl;
a++;
}
else if(num2<num1)
{
cout<<"猜测过小"<<endl;
a++;
}
else
{
cout<<"恭喜猜对了"<<endl;
a++;
break;
}
//猜对 退出游戏
//猜错 提示猜的结果,返回第二步
if(a == 7)
{
cout<<"猜测次数过多,你输了!"<<endl;
}
}
system("pause");
return 0;
}
练习案例:水仙花数
水仙花数是指一个3位数,它的每个位上的数字的三次幂之和等于它本身
例如:1^3+5^3+3^3=153
请用do……while语句,求出所有3位数中的水仙花数
1、将所有的三位数进行输出(100-999)
2、在所有三位数中找到水仙花数
思路分析:
水仙花数
获得个位 153%10=3 直接取模于10
获得十位 153/10=15 15%10=5 先整除10,再取模于10
获得百位 153/100=1
#include<iostream>
using namespace std;
int main()
{
//先打印所有的三位数
int num = 100;
int a = 0; //个位
int b = 0; //十位
int c = 0; //百位
while(num<1000)
{
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++;
}
//从所有的三位数中找到水仙花数
system("pause");
return 0;
}
For循环语句
for(起始表达式;条件表达式;末尾循环体)
{ 循环语句 }
#include<iostream>
using namespace std;
int main()
{
//for循环
//从数字0 打印到 数字9
for(int a=0;a<10;a++)
{
cout<<a<<endl;
}
system("pause");
return 0;
}
案例练习(敲桌子)
从1开始数到100,如果数字个位含有7,或者十位有7,或者是7的倍数,我们打印敲桌子,其余数字直接打印输出
思路分析:
- 先输出1-100
- 从100个数字中找到特殊的数字,改为输出敲桌子
- Num%7=0
Num/10%10=7
Num/10=7
#include<iostream>
using namespace std;
int main()
{
//先输出1-100
int a = 0,b = 0,c = 0;
for(int num = 1;num<101;num++)
{
a = num%7;
b = num/10%10;
c = num/10;
if(a == 0 || b == 7 || c == 7)
{
cout<<"敲桌子"<<endl;
}
else
{
cout<<num<<endl;
}
}
system("pause");
return 0;
}
嵌套循环
#include<iostream>
using namespace std;
int main()
{
//利用嵌套循环实现星图
for(int a = 0;a<10;a++)
{
for(int b = 0;b<10;b++)
{
cout<<"* ";
}
cout<<endl;
}
system("pause");
return 0;
}
练习案例(乘法口诀表)
#include<iostream>
using namespace std;
int main()
{
int a = 1;
for(int i = 1;i<10;i++)
{
for(int j = 1;j <=i;j++)
{
cout<<j<<"*"<<i<<"="<<i*j<<" ";
}
cout<<endl;
}
system("pause");
return 0;
}
跳转语句(用于跳出选择结构或者循环结构)
使用时机:
- 出现在switch条件语句中,作用是终止case并跳出switch
- 出现在循环语句中,作用是跳出当前的循环语句
- 出现在嵌套循环中,跳出最近的内层循环语句
#include<iostream>
using namespace std;
int main()
{
//break的使用时机
//出现在switch语句中
/*cout<<"请选择副本的难度"<<endl;
cout<<"1.普通"<<endl;
cout<<"2.中等"<<endl;
cout<<"3.困难"<<endl;
int selet = 0;
cin>>selet;
switch(selet)
{
case 1:
cout<<"您选择的是普通难度"<<endl;
break;//退出switch语句
case 2:
cout<<"您选择的是中等难度"<<endl;
break;
case 3:
cout<<"您选择的是困难难度"<<endl;
break;
} */
//出现在循环语句中
/*for(int i = 0;i<10;i++)
{
//如果i=5,退出循环,不在打印
if(i==5)
{
break;//退出循环
}
cout<<i<<endl;
}*/
//出现在嵌套循环语句中
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语句
在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环
#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语句
可以无条件跳转语句
如果标记的名称存在,执行到goto语句时,会跳转到标记的位置
一般格式:
goto FLAG;
其他语句
FLAG:
#include<iostream>
using namespace std;
int main()
{
//goto语句
cout<<"1、xxxx"<<endl;
cout<<"2、xxxx"<<endl;
goto FLAG;
cout<<"3、xxxx"<<endl;
cout<<"4、xxxx"<<endl;
FLAG:
cout<<"5、xxxx"<<endl;
system("pause");
return 0;
}
数组
- 数组中每个元素都是相同的数据类型
- 数组是由连续的内存位置组成的
一维数组
- 数据类型 数组名【数组长度】;
- 数据类型 数组名【数组长度】 = {值1,值2…};
- 数据类型 数组名【 】 = {值1,值2…};
#include<iostream>
using namespace std;
int main()
{
//数组
//1、数据类型 数组名[数组长度];
int arr[5];
//给数组中的元素进行赋值
//数组元素的下标是从0开始索引的
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
arr[3] = 40;
arr[4] = 50;
//访问数据元素
cout<<arr[0]<<endl;
cout<<arr[1]<<endl;
cout<<arr[2]<<endl;
cout<<arr[3]<<endl;
cout<<arr[4]<<endl;
//2、数据类型 数组名[数组长度] = {值1,值2...};
//如果在初始化数据时没有全部填写完,那么会用0来填补剩余数据
int arr1[5] = {10,20,30,40,50};
//利用循环输出数组中的元素
for(int i = 0;i<5;i++)
{
cout<<arr1[i]<<endl;
}
//3、数据类型 数组名[ ] = {值1,值2...};
//定义数组时,必须有初始长度
int arr2[] = {10,20,30,40,50};
for(int j = 0;j<5;j++)
{
cout<<arr2[j]<<endl;
}
system("pause");
return 0;
}
一维数组数组名
用途:1、可以统计整个数组在内存中的长度
2、可以获取数组在内存中的首地址
注:首地址:该变量所占的存储区域中的第一个单元的地址
数组名是常量,不可以赋值
#include<iostream>
using namespace std;
int main()
{
//可以通过数组名统计整个数组占用内存大小
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
cout<<"整个数组占用内存空间为:"<<sizeof(arr)<<endl;
cout<<"每个元素占用内存空间为:"<<sizeof(arr[0])<<endl;
cout<<"数组中元素的个数为:"<<sizeof(arr)/sizeof(arr[0])<<endl;
//可以通过数组名查看数组首地址
cout<<"数组的首地址为:"<<arr<<endl;
cout<<"数组中第一个元素地址为:"<<&arr[4]<<endl;
system("pause");
return 0;
}
练习案例1(五只小猪称体重)
在一个数组中记录了五只小猪的体重,如:int arr[5] = {300,350,200,400,250},找到并打印最重的小猪
#include<iostream>
using namespace std;
int main()
{
//创建5只小猪体重的数组
int arr[5] = {300,350,200,400,250};
//从数组中找最大值
int max = 0; //先认定一个最大值0
for(int i = 0;i<5;i++)
{
if(arr[i]>max)
{
max = arr[i];
}
}
//打印最大值
cout<<"最重的小猪体重为:"<<max<<endl;
system("pause");
return 0;
}
练习案例2(数组元素逆置)
请声明一个5个元素的数组,并且将元素逆置
#include<iostream>
using namespace std;
int main()
{
//实现数组元素的逆置
//创建数组
int arr[5] = {1,3,2,5,4};
cout<<"数据逆置前:"<<endl;
for(int i = 0;i<5;i++)
{
cout<<arr[i]<<endl;
}
//实现逆置
//记录起始下标的位置
//记录结束下标的位置
//起始下标与结束下标的元素互换
//起始位置++ 结束位置--
int start = 0; //起始下标
int end = sizeof(arr) / sizeof(arr[0])-1; //结束下标 sizeof(arr)/sizeof(arr[0]) 求出整个数组中元素个数
while(start<end)
{
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
//打印逆置后的数组
cout<<"数据逆置后:"<<endl;
for(int i = 0;i<5;i++)
{
cout<<arr[i]<<endl;
}
system("pause");
return 0;
}
冒泡排序
作用:最常用的排序算法,对数组内元素进行排序
- 比较相邻的元素,若第一个比第二个打,则交换他俩
- 对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值
- 重复以上操作,每次比较次数-1,直到不需要比较
#include<iostream>
using namespace std;
int main()
{
//利用冒泡排序实现升序序列
int arr[9] = {4,2,8,0,5,7,1,3,9};
cout<<"排序前:"<<" ";
for(int i = 0;i<9;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
//开始冒泡排序
//排序总轮数 = 元素个数 - 1
//每轮对比次数 = 元素个数 - 排序轮数 - 1
for(int i = 0;i<9-1;i++) //i 表示总轮数
{
//内层的循环对比
for(int j = 0;j<9-i-1;j++) //j 表示每轮对比数
{
//如果第一个数字比第二个数字大,交换这俩数字
if(arr[j]>arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
//排序后结果
cout<<"排序后:";
for(int i = 0;i<9;i++)
{
cout<<arr[i]<<" ";
}
cout<<endl;
system("pause");
return 0;
}
二维数组
定义方式:
- 数据类型 数组名[行数][列数];
- 数据类型 数组名[行数][列数]={{数据1,数据2},{数据1,数据2}};
- 数据类型 数组名[行数][列数]={数据1,数据2,数据3,数据4};
- 数据类型 数组名[ ][列数]={ 数据1,数据2,数据3,数据4};
二维数组数组名
- 查看二维数组所占内存空间
- 获取二维数组首地址
#include<iostream>
using namespace std;
int main()
{
//二维数组名称用途
//查看占用内存空间大小
int arr[2][3] =
{
{1,2,3},
{4,5,6}
} ;
cout<<"二维数组占用空间:"<<sizeof(arr)<<endl;
cout<<"二维数组第一行占用内存为:"<<sizeof(arr[0])<<endl;
cout<<"二维数组第一个元素占用内存为:"<<sizeof(arr[0][0])<<endl;
cout<<"二维数组行数为:"<<sizeof(arr)/sizeof(arr[0])<<endl;
cout<<"二维数组列数为:"<<sizeof(arr[0])/sizeof(arr[0][0])<<endl;
//可以查看二维数组的首地址
cout<<"二维数组首地址为:"<<arr<<endl;
cout<<"二维数组第一行首地址为:"<<arr[0]<<endl;
cout<<"二维数组第二行首地址为:"<<arr[1]<<endl;
cout<<"二维数组第一个元素首地址:"<<&arr[0][0]<<endl; //查看元素首地址时要加 ‘& ’
cout<<"二维数组第二个元素首地址:"<<&arr[0][1]<<endl;
system("pause");
return 0;
}
练习案例(考试成绩统计)
有三名同学(张三,李四,王五),在一次考试中的成绩分别如下表,请分别输出三名同学的总成绩
语文 | 数学 | 英语 | |
张三 | 100 | 100 | 100 |
李四 | 90 | 50 | 100 |
王五 | 60 | 70 | 80 |
思路:1、创建二维数组(三行三列)
2、统计考试成绩,让每行的三列相加,统计出总和
#include<iostream>
#include<string>
using namespace std;
int main()
{
//创建二维数组
int score[3][3] = {{100,100,100},{90,50,100},{60,70,80}};
string name[3] = {"张三","李四","王五"};
//统计每个人的总和分数
for(int i = 0;i<3;i++)
{
int sum = 0; //统计分数总和变量
for(int j = 0;j<3;j++)
{
sum += score[i][j];
//cout<<score[i][j]<<" ";
}
cout<<name[i]<<"的总分为:"<<sum<<endl;
}
system("pause");
return 0;
}
函数
作用:将一段经常使用的代码封装起来,减少重复代码
函数的定义
步骤:1、返回值类型
2、函数名
3、参数表列
4、函数体语句
5、return 表达式
返回值类型 函数名 (参数列表)
{
函数体语句
return 表达式
}
函数的调用
#include<iostream>
using namespace std;
//定义加法函数
int add(int num1,int num2)
{
int sum = num1 + num2;
return sum;
}
int main()
{
//main函数中调用add函数
int a = 10,b = 20;
//函数调用语法
int c = add(a,b);
cout<<"c = "<<c<<endl;
system("pause");
return 0;
}
函数-值传递
#include<iostream>
using namespace std;
//值传递
//定义函数,实现两个数字进行交换函数
void swap(int num1,int num2)
{
cout<<"交换前:"<<endl;
cout<<"num1 = "<<num1<<endl;
cout<<"num2 = "<<num2<<endl;
int temp = num1;
num1 = num2;
num2 = temp;
cout<<"交换后:"<<endl;
cout<<"num1 = "<<num1<<endl;
cout<<"num2 = "<<num2<<endl;
//返回值不需要的时候可以不写return
}
//如果函数不需要返回值,声明的时候可以写void
int main()
{
int a = 10,b = 20;
//当我们做值传递的时候,函数形参发生改变,并不影响实参
swap(a,b);
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
system("pause");
return 0;
}
函数的常见样式
无参无反
void test1()
{
cout<<“this is test1”<<endl;
}
有参无反
void test2(int a)
{
cout<<“this is test2 a = ”<<a<<endl;
}
有参有反
int test3(int a)
{
cout<<“this is test3 a = ”<<a<<endl;
return 1000;
}
无参有反
void test4()
{
cout<<“this is test4”<<endl; return 1000;
}
函数的声明
#include<iostream>
using namespace std;
//函数的声明
//比较函数,实现两个整型数字进行比较,返回较大的值
//声明可以写多次,但是定义只能一次
int max(int a,int b);
int main()
{
int a = 10,b = 20;
cout<<max(a,b)<<endl;
system("pause");
return 0;
}
int max(int a,int b)
{
return a>b ? a : b; //三目运算符
}
函数的分文件编写
步骤:1、创建后缀名为 .h 的头文件
2、创建后缀名为 .cpp 的源文件
3、在头文件中写函数的声明
4、在源文件中写函数的定义
指针
作用:通过指针间接访问内存
- 内存编号是从0开始记录的,一般用十六进制
- 可以利用指针变量保存地址
#include<iostream>
using namespace std;
int main()
{
//1、定义指针
int a = 10;
//指针定义的语法:数据类型 * 指针变量名;
int *p; //p 代表point
//让指针建立变量a 的地址
p = &a;
cout<<"a的地址为:"<<&a<<endl;
cout<<"p = "<<p<<endl;
//2、使用指针
//可以通过解引用的方式来找到指针指向的内存
//指针前加 * 代表解引用,找到指针指向的内存中的数据
*p = 1000;
cout<<"a = "<<a<<endl;
cout<<"*p = "<<*p<<endl;
system("pause");
return 0;
}
指针所占的内存空间
32位操作系统下占4个字节
64位操作系统下占8个字节
#include<iostream>
using namespace std;
int main()
{
//指针所占内存空间
int a = 10;
int * p = &a;
cout<<"sizeof (int *) = "<<sizeof(int *)<<endl;
cout<<"sizeof (int *) = "<<sizeof(float *)<<endl;
cout<<"sizeof (int *) = "<<sizeof(double *)<<endl;
cout<<"sizeof (int *) = "<<sizeof(char *)<<endl;
system("pause");
return 0;
}
空指针和野指针
空指针:指针变量指向内存中编号为0的空间
用途:初始化指针变量(不知道指向哪,就用空指针)
注:空指针指向的内存不可访问
#include<iostream>
using namespace std;
int main()
{
//空指针
//空指针不可访问,0~255之间的内存编号是系统占用的,因此不可访问
int * p = NULL; //NULL代表0
*p = 100;
system("pause");
return 0;
}
野指针:指针变量指向非法的内存空间
总:空指针和野指针都不是我们申请的空间,因此不要访问
const修饰指针
三种情况:
1、const修饰指针——常量指针
const int * p = &a;
(指针的指向可以修改,指向的值不可以改)
2、const修饰常量——指针常量
int * const p = &a;
(指针的指向不可以改,指向的值可以改)
3、const既修饰常量,又修饰指针
const int * const p = &a;
(指针的指向和值都不可改)
指针和数组
作用:利用指针访问数组元素
#include<iostream>
using namespace std;
int main()
{
//利用指针访问数组元素
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
cout<<"第一个元素为:"<<arr[0]<<endl;
int * p = arr; //arr就是数组首地址
/*cout<<"利用指针访问第一个元素"<<*p<<endl;
p++; //让指针向后偏移4个字节
cout<<"利用指针访问第二个元素"<<*p<<endl;*/
for(int i = 0;i<10;i++)
{
cout<<*p<<endl;
p++;
}
system("pause");
return 0;
}
指针和函数
作用:利用指针作函数参数,可以修改实参的值
#include<iostream>
using namespace std;
//实现两个数字进行交换
void swap(int a,int b)
{
int temp = a;
a = b;
b = temp;
cout<<"swap a = "<<a<<endl;
cout<<"swap b = "<<b<<endl;
}
void swap1(int * p1,int * p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main()
{
//指针和函数
//1、值传递
int a = 10;
int b = 20;
//swap(a,b)
//地址传递
//如果是地址传递,可以修饰实参
//地址本身不变,但是地址所对应的元素交换,所有可以修饰实参
swap1(&a,&b);
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
//2、地址传递
cout<<
system("pause");
return 0;
}
练习案例(指针,数组,函数)
封装一个函数,利用冒泡排序,实现对整型数组的升序排序
(例如: int arr[10]={4,3,6,9,1,2,10,8,7,5};)
#include<iostream>
using namespace std;
//冒泡排序函数 参数1:数组首地址 参数2:数组长度
void balabala(int *arr,int len)
{
for(int i = 0;i<len-1;i++) //排序总轮数=元素个数-1
{
for(int j = 0;j<len - i - 1;j++) //每轮对比次数=元素个数-排序总轮数-1
{
//如果j>j+1的值,交换数字
if(arr[j]>arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
//打印数组函数
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]);
//2、创建一个函数实现冒泡排序
balabala(arr,len);
//3、打印排序后的数组
printArray(arr,len);
system("pause");
return 0;
}
结构体
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
定义: struct 结构体名 {结构体成员列表}
方式: 1、struct 结构体名 变量名
2、struct 结构体名 变量名 = {成员1值,成员2值…}
3、定义结构体时顺便创建变量
#include<iostream>
#include<string>
using namespace std;
//1、创建学生数据类型 学生包括:姓名,年龄,分数
//自定义的数据类型,一些类型集合组成的一个类型
struct student
{
//成员列表
//姓名
string name;
//年龄
int age;
//分数
int score;
}s3; //顺便创建结构体变量
//2、通过学生的类型创建具体的学生
int main()
{
//2.1 struct student s1
//struct 关键字可以省略 //student s1;
struct student s1;
//给s1属性赋值,通过.访问结构体变量中的属性
s1.name = "张三";
s1.age = 18;
s1.score = 100;
cout<<"姓名:"<<s1.name<<" 年龄:"<<s1.age<<" 分数:"<<s1.score<<endl;
//2.2 struct student s2 = {...}
struct student s2 = {"李四",19,80};
cout<<"姓名:"<<s2.name<<" 年龄:"<<s2.age<<" 分数:"<<s2.score<<endl;
//2.3 在定义结构体的时候顺便创建结构体变量
s3.name = {"王五"};
s3.age = 20;
s3.score = 60;
cout<<"姓名:"<<s3.name<<" 年龄:"<<s3.age<<" 分数:"<<s3.score<<endl;
system("pause");
return 0;
}
结构体数组
作用:将自定义的结构体放入到数组中方便维护
语法:struct 结构体 数组名[元素个数] = {{},{},…{}};
#include<iostream>
#include<string>
using namespace std;
//结构体数组
//1、创建(定义)一个结构体
struct student
{
//姓名
string name;
//年龄
int age;
//分数
int score;
};
int main()
{
//2、创建结构体数组
struct student arr[3] =
{
{"张三",18,100},
{"李四",19,80},
{"王五",20,60}
};
//3、给结构体数组中的元素赋值
arr[2].name = "赵六";
arr[2].age = 38;
arr[2].score = 88;
//4、遍历结构体数组
for(int i = 0;i<3;i++)
{
cout<<"姓名:"<<arr[i].name
<<" 年龄:"<<arr[i].age
<<" 分数:"<<arr[i].score<<endl;
}
system("pause");
return 0;
}
结构体指针
作用:通过指针访问结构体中的成员
利用操作符 -> 可以通过结构体指针访问结构体属性
#include<iostream>
#include<string>
using namespace std;
//结构体指针
struct student
{
string name;
int age;
int score;
};
int main()
{
//创建学生的结构体变量
struct student s = {"张三",18,100};
//通过指针指向结构体变量
struct student *p = &s;
//通过指针访问结构体变量中的数据
//通过结构体指针访问结构体中的属性,需要利用 ->
cout<<"姓名:"<<p->name<<" 年龄:"<<p->age<<" 分数:"<<p->score<<endl;
system("pause");
return 0;
}
结构体嵌套结构体
作用:结构体中的成员可以是另一个结构体
#include<iostream>
#include<string>
using namespace std;
//定义学生结构体
struct student
{
string name;
int age;
int score;
};
//定义老师结构体
struct teacher
{
int id;
string name;
int age;
struct student stu; //辅导的学生
};
int main()
{
//结构体嵌套结构体
//创建老师
teacher t;
t.id = 1234567;
t.name = "老王";
t.age = 54;
t.stu.name = "张三";
t.stu.age = 18;
t.stu.score = 89;
cout<<"老师姓名:"<<t.name<<" 老师编号:"<<t.id<<" 老师年龄:"<<t.age<<endl
<<"老师辅导的学生姓名:"<<t.stu.name<<" 学生年龄:"<<t.stu.age<<" 学生分数:"<<t.stu.score<<endl;
system("pause");
return 0;
}
结构体做函数参数
作用:将结构体作为参数向函数中传递
传递方式:1、值传递 2、地址传递
#include<iostream>
#include<string>
using namespace std;
//定义学生的结构体
struct student
{
string name;
int age;
int score;
};
//1、值传递 形参发生改变,实参不受影响
void printstu1(struct student s)
{
s.age = 100;
cout<<"子函数中 姓名:"<<s.name<<" 年龄:"<<s.age<<" 成绩:"<<s.score<<endl;
};
//2、地址传递 实参随形参一起改变
void printstu2(struct student *p)
{
p->age = 200;
cout<<"子函数中 姓名:"<<p->name<<" 年龄:"<<p->age<<" 成绩:"<<p->score<<endl;
};
int main()
{
//结构体做函数参数
//将学生传入到一个参数中,打印学生的所有信息
//创建结构体变量
struct student s;
s.name = "张三";
s.age = 13;
s.score = 86;
cout<<"姓名:"<<s.name<<" 年龄:"<<s.age<<" 成绩:"<<s.score<<endl;
printstu1(s);
printstu2(&s); //传输地址
cout<<"姓名:"<<s.name<<" 年龄:"<<s.age<<" 成绩:"<<s.score<<endl;
system("pause");
return 0;
}
结构体中const的使用场景
作用:用const来防止误操作(限定只读状态,防止修改)
#include<iostream>
#include<string>
using namespace std;
//const 的使用场景
struct student
{
string name;
int age;
int score;
};
//将函数中的形参改为指针,可以减少内存空间,而且不会复制新的副本出来
void printstu(const student *s) //加入const后,一旦有操作就会报错,可以防止我们的误操作
{
//s->age = 150; 会报错
cout<<"姓名:"<<s->name<<" 年龄:"<<s->age<<" 成绩:"<<s->score<<endl;
};
int main()
{
struct student s = {"张三",15,89};
printstu(&s);
system("pause");
return 0;
}
/*void printstu(student s[3]) //值传递
{
for(int i = 0;i<3;i++)
{
cout<<"姓名:"<<s[i].name<<" 年龄:"<<s[i].age<<" 成绩:"<<s[i].score<<endl;
}
};
int main()
{
//创建结构变量
struct student s[3] =
{
{"张三",15,89},
{"李四",16,53},
{"王五",18,86}
};
//通过函数打印结构体变量信息
printstu(s);
system("pause");
return 0;
}*/
结构体案例1
1、学校正在做毕设项目,每名老师带领5名学生,共3名老师,需求如下:
设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员学生的成员有姓名、成绩,创建数组存放3名老师,通过函数给每个老师及所带学生赋值,最终打印出老师数据以及老师所带的学生数据。
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
//学生的结构体
struct student
{
//姓名
string sname;
//分数
int score;
};
//老师的结构体定义
struct teacher
{
//姓名
string tname;
//学生数组
struct student s[5];
};
//给老师和学生赋值的函数
void balabala(struct teacher t[] , int len)
{
string nameseed = "ABCDE";
//给老师开始赋值
for(int i = 0;i<len;i++)
{
t[i].tname = "teacher_";
t[i].tname += nameseed[i];
//通过循环给每名老师所带的学生赋值
for(int j = 0;j<5;j++)
{
t[i].s[j].sname = "student_";
t[i].s[j].sname += nameseed[j];
int random = rand() % 61 + 40;
t[i].s[j].score = random;
}
}
}
void printInfo(struct teacher t[], int len)
{
for(int i = 0;i<len;i++)
{
cout << "老师的姓名:" << t[i].tname << endl;
for (int j = 0;j<5;j++)
{
cout << "\t学生姓名:" << t[i].s[j].sname <<
" 考试分数:" << t[i].s[j].score << endl;
}
}
}
int main()
{
//随机数种子
srand((unsigned int)time(NULL));
//1、创建3名老师的数组
struct teacher t[3];
//2、通过函数给3名老师的信息赋值,并给老师带的学生信息赋值
int len = sizeof(t) / sizeof(t[0]);
balabala(t,len);
//3、打印所有老师及所带的学生信息
printInfo(t,len);
system("pause");
return 0;
}
案例练习2
设计一个英雄的结构体,包括姓名、年龄、性别;创建结构体数组,数组中存放5名英雄。通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果。
五名英雄信息如下:
{刘备,23,男} {关羽,22,男}
{张飞,20,男} {赵云,21,男} {貂蝉,19,女}
#include<iostream>
#include<string>
using namespace std;
//创建英雄的结构体
struct hero
{
string name;
int age;
string sex;
};
//冒泡排序函数并打印输出
void bala(struct hero san[],int len)
{
for(int i = 0;i<len-1;i++)
{
for(int j = 0;j<len-i-1;j++)
{
//如果j下标的元素年龄 大于 j+1下标的元素年龄,交换
if(san[j].age>san[j+1].age)
{
struct hero temp = san[j];
san[j] = san[j+1];
san[j+1] = temp;
}
}
}
};
void printall(struct hero san[],int len)
{
cout<<"按照年龄排序:"<<endl;
for(int i = 0;i<len;i++)
{
cout<<"姓名:"<<san[i].name<<" 年龄:"<<san[i].age<<" 性别:"<<san[i].sex<<endl;
}
};
int main()
{
//创建结构体数组
struct hero san[5] =
{
{"刘备",23,"男"},{"关羽",22,"男"},
{"张飞",20,"男"},{"赵云",21,"男"},
{"貂蝉",19,"女"}
};
int len = sizeof(san)/sizeof(san[0]);
for(int i = 0;i<5;i++)
{
cout<<"姓名:"<<san[i].name<<" 年龄:"<<san[i].age<<" 性别:"<<san[i].sex<<endl;
}
bala(san,len);
printall(san,len);
system("pause");
return 0;
}
内存分区模型
- 代码区:存放函数体的二进制代码,由操作系统进行管理的
- 全局区:存放全局变量、静态变量、常量
静态变量:在普通变量前加static
常量:字符串常量、const修饰的常量
Const修饰的常量:全局、局部
- 栈区:由编译器自动分配释放,存放函数的参数值、局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
C++在程序运行前分为全局区和代码区
代码区特点是共享和只读
全局区中存放全局变量、静态变量、常量
常量区中存放const修饰的常量和字符串常量
栈区:由编译器自行释放,存放函数的参数值、局部变量等
堆区:利用new关键字,可以将数据开辟到堆区
注:不要返回局部变量的地址,栈区的数据由编译器管理开辟和释放
- 指针本质也是局部变量,放在栈上,指针保存的数据放在堆区
- 野指针:访问一个已销毁或者访问受限的内存区域的指针,野指针不能判断是否为NULL来避免
- 垂悬指针:指针正常初始化,曾指向一个对象,该对象被销毁了,但是指针未制空,那么就成了悬空指针。
- 堆区开辟的数据,由程序员手动开辟,手动释放,释放利用delete操作符
引用
本质:变量别名
语法:数据类型 & 别名 = 原名
注:1、引用必须要初始化
nt &b; //错误
2、引用一旦确定,不可改变
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int& b = a; //初始化
int c = 20; //不可更改
b = c; //赋值操作,不是更改引用
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
system("pause");
return 0;
}
引用的本质:指针常量
#include<iostream>
using namespace std;
void test(const int& a)//防止实参被修改
{
//int a = 100; 不可被修改
cout << "a = " << a << endl;
}
int main()
{
int a = 1000;
test(a);
system("pause");
return 0;
}
函数的默认参数
#include<iostream>
using namespace std;
//函数的默认参数
//如果传入了新数据,就用自己的数据,若没有,则用默认参数
//如果某个位置已经有了默认参数,那么从这个位置后,从左到右都必须有默认参数值
//如果函数声明有了默认参数,函数实现就不能有默认参数,声明和实现只能有一个默认参数
int test(int a, int b = 1, int c = 2)
{
return a + b + c;
}
int main()
{
int a = 1, b = 2, c = 3;
cout << test(a, 2) << endl;
system("pause");
return 0;
}
函数重载(提高函数复用性)
条件: 1、作用于统一作用域下
2、函数名称相同
3、函数参数类型不同,或者个数不同,顺序不同
#include<iostream>
using namespace std;
void func()
{
cout << "1" << endl;
}
void func(int a)
{
cout << "1.1" << endl;
}
//函数重载遇到默认参数
void func1(int a, int b = 10)
{
cout << "2" << endl;
}
void func1(int a)
{
cout << "2.1" << endl;
}
int main()
{
int a = 10;
func(a);
//func1(a); 出现二义性,报错,尽量避免这种情况出现
system("pause");
return 0;
}
类和对象
C++面向对象三大特性:封装、继承、多态
封装的意义: 将属性和行为作为一个整体表现事物
将属性和行为加以权限控制
语法:class 类名{访问权限 :属性/行为};
(行为通常用函数)
#include<iostream>
using namespace std;
//设计一个圆,求圆的周长
const double pai = 3.14;
//class代表设计一个类,类后面紧跟着的就是类名称
class circle
{
//访问权限
public:
//属性 半径
int a;
//行为 通常用函数
double circlelatec()
{
return 2 * pai * a;
}
};
int main()
{
//通过圆类创建具体的圆
circle c1;
//给圆的对象的属性进行赋值
c1.a = 10;
//调用函数
cout << "圆的周长为:" << c1.circlelatec() << endl;
system("pause");
return 0;
}
//设计一个学生类,属性有姓名、学号,可以给姓名、学号赋值,可以显示姓名、学号
#include<iostream>
#include<string>
using namespace std;
class student
{
//访问权限
public:
//属性
string name;
int id;
//行为
//函数的声明
student();//构造函数
void input();//信息输入函数
void out();//信息输出函数
};
student::student()
{
name = "no name";
id = 0;
}
void student::input()
{
cout << "姓名:";
cin >> name;
cout << "学号:";
cin >> id;
}
void student::out()
{
cout << "姓名:" << name << endl;
cout << "学号:" << id << endl;
}
int main()
{
char x = '0';
//创建对象
student a;
student b[20];
cout << "请输入学生信息:" << endl;
for (int i = 0; x != 'n'; i++)
{
cout << i + 1 << "号";
b[i].input();
cout << "按下‘n’结束" << endl;
cin >> x;
}
cout << "所有人的信息是:" << endl;
for (int j = 0; j < 20; j++)
{
b[j].out();
}
system("pause");
return 0;
}
继承
公有public:类内可以访问,类内也可以访问
保护protected:类内可以访问,类外不可以访问
私有private:类内可以访问,类外不可以访问
//继承中的对象模型
#include<iostream>
using namespace std;
class fath
{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class son :public fath
{
public:
int m_d;
};
//在父类中所有的非静态成员都会被子类继承下来,父类中私有的属性,被编译器隐藏了,因此访问不到
int main()
{
cout << sizeof(son) << endl;
system("pause");
return 0;
}
struct和class的区别
默认的访问权限不同:
struct默认权限为公有Class默认为私有
成员属性设置为私有
优点: 1、可以自己控制读写数据
2、可以检测数据的有效性
#include<iostream>
using namespace std;
#include<string>
class person
{
public:
void setname(string name)//可读可写 写
{
m_name = name;
}
string showname() //读
{
return m_name;
}
int showage() //只读
{
m_age = 18;
return m_age;
}
private:
string m_name;
int m_age;
int m_id;
};
int main()
{
person p;
p.setname("张三");
cout << "姓名:" << p.showname() << endl;
cout << "年龄:" << p.showage() << endl;
system("pause");
return 0;
}
例1:封装一个长方体类
#include<iostream>
using namespace std;
class cube
{
public:
//设置高
void seth(int h)
{
m_h = h;
}
//显示高
int showh()
{
return m_h;
}
void setl(int l)
{
m_l = l;
}
int showl()
{
return m_l;
}
void setw(int w)
{
m_w = w;
}
int showw()
{
return m_w;
}
//获取面积
int s()
{
return m_h * m_l * 2 + m_h * m_w * 2 + m_l * m_w * 2;
}
//获取体积
int v()
{
return m_h * m_l * m_w;
}
private:
int m_h;
int m_l;
int m_w;
};
int main()
{
cube s;
s.seth(1);
s.setl(2);
s.setw(3);
cout << "长方体面积:" << s.s() << endl;
cout << "长方体体积:" << s.v() << endl;
system("pause");
return 0;
}
例2:判断点与圆的位置关系
要求:创建点与圆类 判断位置换下
#include<iostream>
using namespace std;
class point
{
public:
int m_x;
int m_y;
//函数的声明
void setx(int x);
void sety(int y);
int showx();
int showy();
};
void point::setx(int x)
{
m_x = x;
}
int point::showx()
{
return m_x;
}
void point::sety(int y)
{
m_y = y;
}
int point::showy()
{
return m_y;
}
class circle
{
public:
int m_r;
point m_center;
//函数的声明
void setr(int r);
void setcenter(point center);
int showr();
point showcenter();
};
void circle::setr(int r)
{
m_r = r;
}
int circle::showr()
{
return m_r;
}
void circle::setcenter(point center)
{
m_center = center;
}
point circle::showcenter()
{
return m_center;
}
void isincir(circle& c, point& p)
{
//计算两点间距离的平方
int distence = (c.showcenter().showx() - p.showx()) * (c.showcenter().showx() - p.showx()) +
(c.showcenter().showy() - p.showy()) * (c.showcenter().showy() - p.showy());
//计算半径的平方
int rdistence = c.showr() * c.showr();
//判断半径与两点间距离的大小
if (distence == rdistence)
{
cout << "点在圆上" << endl;
}
else if (distence > rdistence)
{
cout << "点在圆外" << endl;
}
else
{
cout << "点在圆内" << endl;
}
}
int main()
{
point p;
p.setx(10);
p.sety(10);
circle c;
c.setr(10);
point center;
center.setx(10);
center.sety(0);
c.setcenter(center);
//判断关系
isincir(c, p);
system("pause");
return 0;
}
构造函数和析构函数
对象的初始化和清理(安全问题):
- 一个对象或者变量没有初始状态,对其使用后果是未知的
- 使用完一个对象或变量,没有及时清理,也存在安全问题
C++利用构造函数和析构函数解决上述问题,若我们不提供构造和析构,编译器会提供,编译器提供的得构造和析构函数是空实现的。
构造函数:作用于创造对象时为对象的成员赋值,构造函数由编译器自动调用,无需手动调用
析构函数:作用于对象销毁前系统自动调用,执行一些清理工作
构造函数语法: 类名(){ }
1、构造函数没有返回值,也不写void2、函数名称与类名相同
3、构造函数可以有参数,可以发生重载
4、程序在调用对象时候会自动调用构造,无需手动调用,而且只调用一次
析构函数语法: ~类名(){ }
1、析构函数没有返回值,也不写void
2、函数名称与类名相同,在名称前加 ~ 符号
3、析构函数不可以有参数,不可以发生重载
4、程序在对象对象时会自动调用析构,无需手动调用,而且只调用一次
#include<iostream>
using namespace std;
class person
{
public:
person()
{
cout << "构造函数" << endl;
}
~person()
{
cout << "析构函数" << endl;
}
};
void s()
{
person p;
}
int main()
{
person p;
system("pause");
return 0;
}
构造函数的分类和调用
两种分类方式:
按照参数分:有参构造和无参构造
按照类型分:普通构造和拷贝构造
三种调用方式:
括号法 显示法 隐式转换法
#include<iostream>
using namespace std;
class person
{
public:
int m_age;
//无参构造
person()
{
cout << "无参构造" << endl;
}
//有参构造
person(int age)
{
m_age = age;
cout << "有参构造" << endl;
}
//拷贝函数
person(const person &p)
{
m_age = p.m_age;
cout << "123" << endl;
}
~person()
{
cout << "析构函数" << endl;
}
};
//调用函数
void d()
{
//1、括号法
person p1; //默认构造函数调用(无参)
person p2(10); //默认构造函数调用(有参)
person p3(p2); //拷贝函数调用
//2、显示法
person p4;
person p5 = person(10);
person p6 = person(p2);
//3、隐式转换法
person p7 = 10; //相当于写了 person p7 = person(10); 有参函数
person p8 = p7; //拷贝构造
}
int main()
{
d();
system("pause");
return 0;
}
拷贝构造函数调用时机
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
#include<iostream>
using namespace std;
//拷贝构造函数的调用时机
class person
{
public:
int m_age;
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;
}
};
//1、使用一个已经创建完毕的对象来初始化一个新对象
void a()
{
person p1(10);
person p2(p1);
cout << "年龄:" << p2.m_age << endl;
}
//2、值传递的方式给函数参数传值
void dowork(person p)
{
}
void b()
{
person p;
dowork(p);
}
//3、值方式返回局部对象
person dowork1()
{
person p3;
cout << (int*)&p3 << endl;
return p3;
}
void c()
{
person p = dowork1();
cout << (int*)&p << endl;
}
int main()
{
a();
b();
system("pause");
return 0;
}
深拷贝和浅拷贝
浅拷贝:简单的赋值操作(编译器帮助进行,无需定义)
深拷贝:在堆区重新申请空间,进行拷贝操作
#include<iostream>
using namespace std;
//深拷贝和浅拷贝
class person
{
public:
int m_age;
int* m_hight; // 通过指针将数据开辟到堆区
person()
{
cout << "person默认构造函数的调用" << endl;
}
person(int age,int hight)
{
m_hight = new int(hight); //通过new关键字将数据开辟到堆区
m_age = age;
cout << "person有参构造函数的调用" << endl;
}
//自己创建深拷贝,来解决浅拷贝的问题
person(const person& p)
{
cout << "拷贝构造函数调用" << endl;
//m_hight = p.m_hight; 编译器自动实现
m_age = p.m_age;
//深拷贝
m_hight = new int(*p.m_hight);
}
~person()
{
//析构函数 将堆区开辟的数据进行释放操作
if (m_hight != NULL)
{
delete m_hight;
m_hight = NULL;
}
cout << "析构函数的调用" << endl;
}
};
void test1()
{
person p1(18,134);
cout << "年龄:" << p1.m_age << endl;
cout << "体重;" << *p1.m_hight << endl;
person p(p1); //浅拷贝
cout << "年龄:" << p.m_age << endl;
cout << "体重;" << *p.m_hight << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
总结:如果属性有在堆区开辟的,那么一定要自己去构建拷贝调用函数,防止浅拷贝带来的问题。
初始化列表
语法:构造函数():属性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;
}*/
//初始化列表
person(int a, int b, int c) :m_a(a), m_b(b), m_c(c)
{
}
};
void test()
{
person p(1,2,3);
cout << p.m_a << " " << p.m_b << " " << p.m_c << endl;
}
int main()
{
test();
system("pause");
return 0;
}
类对象作为类成员
C++类的成员可以是另一个类的对象,称为对象成员
#include<iostream>
#include<string>
using namespace std;
//类对象作为类成员
//当其他类对象作为本类成员,构造时候先构造类对象,再构造自身,析构顺序与之相反
class phone
{
public:
//手机品牌名称
string m_pname;
phone(string pname)
{
m_pname = pname;
cout << "phone的构造函数调用" << endl;
}
~phone()
{
cout << "phone析构函数的调用" << endl;
}
};
class person
{
public:
string m_name;
phone m_phone;
person(string name, string pname) :m_name(name), m_phone(pname)
{
cout << "person构造函数的调用" << endl;
}
~person()
{
cout << "person析构函数调用" << endl;
}
};
void test()
{
person p("张三", "华为");
cout << p.m_name << " " << p.m_phone.m_pname << endl;
}
int main()
{
test();
system("pause");
return 0;
}
静态成员
静态成员就是在成员变量和成员函数前加上关键字static
静态成员:
静态成员变量:
所有对象共享一份数据,在编译阶段分配内存,类内申明,类外初始化
#include<iostream>
using namespace std;
//静态成员变量
class perosn
{
public:
/*1、所有对象共享同一份数据
2、在编译阶段分配内存
3、类内声明,类外初始化*/
static int m_a;
};
int perosn::m_a = 12;
void test()
{
/*静态成员变量不属于某个对象,所有对象都共享同一份数据
因此静态成员有两种访问方式*/
//1、通过对象进行访问
perosn p;
cout << p.m_a << endl;
/*2、通过类名进行访问
cout<<perosn::m_a<<endl; */
}
int main()
{
test();
system("pause");
return 0;
}
静态成员函数:
所有对象共享同一个函数,静态成员函数只能访问静态成员变量
#include<iostream>
using namespace std;
class person
{
public:
static void func()
{
m_a = 12;
cout << "静态成员函数" << endl;
cout << m_a << endl;
}
static int m_a;
};
int person::m_a = 13;
void test()
{
person p;
p.func();
}
int main()
{
test();
system("pause");
return 0;
}
this指针概念
this 指针指向被调用的成员函数所属的对象
this 指针隐含在每一个非静态成员函数内,无需定义,直接使用
用途:在形参和成员变量同名时,可用this指针区分
在类的非静态成员函数中返回对象本身,可使用 return *this
#include<iostream>
using namespace std;
class person
{
public:
person(int age)
{
//this指针指向被调用的成员函数所属的对象
this->age = age;
}
person& personaddage(person &p1)//返回本体,需要以引用的方式定义
{
this->age += p1.age;
//this指向p2的指针,*this指向p2的对象本体
return *this;
}
int age;
};
//解决名称冲突
void test0()
{
person p(12);
cout << "p1的年龄:" << p.age << endl;
}
//返回对象本身
void test1()
{
person p1(10);
person p2(10);
//链式编程思想
p1.personaddage(p2).personaddage(p2).personaddage(p2);
cout << "p1 p2年龄和为:" << p1.age << endl;
}
int main()
{
test0();
test1();
system("pause");
return 0;
}
Const修饰成员函数
常函数:1、常函数不可修改成员属性
2、成员函数声明时加关键字mutable,在常函数中仍可修改
常对象:常对象只能调用常函数
#include<iostream>
using namespace std;
class person
{
public:
void showperson()const
{
//this指针的本质是指针常量 指针的指向是不可修改的
//const person * const this
//在成员函数后加const,修饰的是指针指向,使指针的值也不可修改
this->m_age1 = 1;
/*this->m_age = 1;
this = NULL; this指针不可修改指针的指向 */
}
void func()
{
}
int m_age;
mutable int m_age1; //特殊变量,即使在常函数中也可以修改值 加关键字 mutable
};
void test()
{
person p;
p.showperson();
}
//常对象
void test1()
{
const person p1;
//p1.m_age = 1;
p1.m_age1 = 1; //m_age1为特殊值,在常对象、常函数中都可修改
//常对象只能调用常函数
p1.showperson();
//p1.func(); 常对象不可调用普通成员函数,因为普通成员函数可以修改属性
}
int main()
{
test();
system("pasue");
return 0;
}
友元
友元的关键字为:friend
友元的三种实现:
- 全局函数做友元
- 类做友元
- 成员函数做友元
#include<iostream>
#include<string>
using namespace std;
//建筑类
class buliding
{
//标明友军,可以访问类中的私有成员
friend void goodguy(buliding* buliding);
public:
string m_sittingroom;
buliding()
{
m_sittingroom = "客厅";
m_bedroom = "卧室";
}
private:
string m_bedroom;
};
//全局函数
void goodguy(buliding *buliding)
{
cout << "1、全局函数正在访问" << buliding->m_sittingroom << endl;
cout << "1、全局函数正在访问" << buliding->m_bedroom << endl;
}
void test()
{
buliding buliding;
goodguy(&buliding);
}
int main()
{
test();
system("pause");
return 0;
}
#include<iostream>
#include<string>
using namespace std;
class Buliding;
class good;
void test();
class good
{
public:
good();
void visit(); //让visit函数可以访问buliding中的私有成分
void visit1(); //让visit1函数无法访问buliding中的私有成分
Buliding* buliding;
};
class Buliding
{
friend void good::visit();
public:
Buliding();
string sittingroom;
private:
string bedroom;
};
//类外实现函数
Buliding::Buliding()
{
sittingroom = "客厅";
bedroom = "卧室";
}
good::good()
{
buliding = new Buliding;
}
void good::visit()
{
cout << "公共:" << buliding->sittingroom << endl;
cout << "私有:" << buliding->bedroom << endl;
}
void good::visit1()
{
cout << "公共:" << buliding->sittingroom << endl;
}
void test()
{
good g;
g.visit();
g.visit1();
}
int main()
{
test();
system("pause");
return 0;
}
运算符重载
给运算符重定义,赋予其另一种功能,以适应不同的数据类型
加法运算符重载
作用:实现两个自定义数据类型相加
#include<iostream>
using namespace std;
//加号运算符重载
//成员函数重载
class person
{
public:
//成员函数重载+
//person operator+(person& p)
//{
// person temp;
// temp.m_a = this->m_a + p.m_a;
// temp.m_b = this->m_b + p.m_b;
// return temp;
//}
int m_a;
int m_b;
};
//全局函数重载+
person operator+(person &p1,person &p2)
{
person temp;
temp.m_a = p1.m_a + p2.m_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
void test()
{
person p1;
p1.m_a = 10;
p1.m_b = 10;
person p2;
p2.m_a = 10;
p2.m_b = 10;
person p3 = p1 + p2;
cout << p3.m_a << " " << p3.m_b << endl;
}
int main()
{
test();
system("pause");
return 0;
}
左移运算符重载
作用:输出自定义类型
#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;
}
private:
//利用成员函数重载 左移运算符 p.operator<<(cout) 简化:p<<cout
//不会利用成员函数重载<<运算符,因为无法实现cout 在左边
//void operator<<()
int m_a;
int m_b;
};
//只能利用全局函数重载 ostream标准的输出流对象
ostream & operator<<(ostream &cout,person &p)
{
cout << p.m_a << " " << p.m_b;
return cout;
}
void test()
{
person p(10, 10);
cout << p << endl;
}
int main()
{
test();
system("pause");
return 0;
}
总结:重载左移运算符配合友元函数可以实现自定义数据类型
递增运算符重载
作用:通过递增运算符重载,实现自己的整型数据
#include<iostream>
using namespace std;
//重载递增运算符
class myint
{
friend ostream& operator<<(ostream& cout, myint myi);
public:
myint()
{
m_num = 0;
}
//重载前置++运算符 返回引用是为了一直对一个数据进行操作
myint& operator++()
{
//先进行++操作
m_num++;
//再做自身的返回
return *this;
}
//重载后置++运算符
myint operator++(int) //int表示占位函数,可以用于区分前置和后置递增
//返回的是局部对象的引用,局部对象在当前函数执行结束后被系统释放
//所以后置递增返回值,而不是地址
{
//先记录当前结果
myint temp = *this;
//后递增
m_num++;
//最后将记录结果做返回
return temp;
}
private:
int m_num;
};
ostream& operator<<(ostream& cout, myint myi)
{
cout << myi.m_num;
return cout;
}
void test()
{
myint myi;
cout << ++(++myi) << endl;
cout << myi << endl;
}
void test1()
{
myint myi;
cout << myi++ << endl; //先输出myi,再进行++操作
cout << myi << endl; //因此这里的myi为myi++的值
}
int main()
{
test();
test1();
//int a = 0;
//cout << ++(++a) << endl;
//cout << a << endl;
system("pause");
return 0;
}
赋值运算符重载
C++编译器至少给一个类提供4个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
- 赋值运算符 operator = 对属性进行值拷贝
#include<iostream>
using namespace std;
//重载赋值运算符
class person
{
public:
person(int age)
{
m_age = new int(age); //利用new关键字将数据开辟到堆区,堆区的数据需要由程序员自行释放
}
~person()
{
cout << "析构函数" << endl;
if (m_age != NULL)
{
delete m_age; //利用delete关键字,将内存释放
m_age = NULL;
}
}
person& operator=(person& p)
{
//先判断是否有堆区的数据,然后进行释放,再进行深拷贝
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
//深拷贝
m_age = new int(*p.m_age);
//返回对象本身
return *this;
}
int *m_age;
};
void test()
{
person p(12);
person p1(18);
person p2(13);
p2 = p1 = p;
cout << *p.m_age << endl; // * 为解引用,若无解引用,输出的为地址
cout << *p1.m_age << endl;
cout << *p2.m_age << endl;
}
int main()
{
test();
system("pause");
return 0;
}
关系运算符重载
作用:让两个自定义的数据类型进行对比操作
#include<iostream>
using namespace std;
class person
{
friend void test();
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;
}
else
{
return false;
}
return this;
}
bool operator!=(person& p)
{
if (this->m_name != p.m_name && this->m_age != p.m_age)
{
return true;
}
else
{
return false;
}
}
private:
int m_age;
string m_name;
};
void test()
{
person p1("Tom", 18);
person p2("aom", 12);
cout << p1.m_age << " " << p1.m_name << endl;
cout << p2.m_age << " " << p2.m_name << endl;
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()
{
test();
system("pause");
return 0;
}
函数调用运算符重载
结束后立即释放,因此匿名函数比非匿名函数更节省空间
#include<iostream>
using namespace std;
//函数运算符重载 打印输出类
class print
{
public:
void operator()(string test)
{
cout << test << endl;
}
};
void test()
{
print p;
p("hello world");
}
//加法类
class add
{
public:
void operator()(int num1,int num2)
{
cout << num1 + num2 << endl;
}
};
void test1()
{
add a;
a(1, 2);
}
int main()
{
test();
test1();
system("pause");
return 0;
}
继承
继承语法:class 子类 :继承方式 父类
子类也称派生类,父类也称基类
#include<iostream>
using namespace std;
//普通实现画面
//class java
//{
//public:
// void head()
// {
// cout << "首页、公开课...." << endl;
// }
// void foot()
// {
// cout << "帮助中心、交流合作..." << endl;
// }
// void left()
// {
// cout << "java、c++...." << endl;
// }
// void content()
// {
// cout << "java视频" << endl;
// }
//};
//class python
//{
//public:
// void head()
// {
// cout << "首页、公开课...." << endl;
// }
// void foot()
// {
// cout << "帮助中心、交流合作..." << endl;
// }
// void left()
// {
// cout << "java、c++...." << endl;
// }
// void content()
// {
// cout << "python视频" << endl;
// }
//};
//class cpp
//{
//public:
// void head()
// {
// cout << "首页、公开课...." << endl;
// }
// void foot()
// {
// cout << "帮助中心、交流合作..." << endl;
// }
// void left()
// {
// cout << "java、c++...." << endl;
// }
// void content()
// {
// cout << "c++视频" << endl;
// }
//};
//void test()
//{
// cout << "Java下载视频页面如下:" << endl;
// java j;
// j.head();
// j.foot();
// j.left();
// j.content();
// cout << "------------------------------" << endl;
// cout << "Python下载视频页面如下:" << endl;
// python p;
// p.head();
// p.foot();
// p.left();
// p.content();
// cout << "------------------------------" << endl;
// cout << "C++下载视频页面如下:" << endl;
// cpp c;
// c.head();
// c.foot();
// c.left();
// c.content();
//}
//利用继承实现页面的显示
class basepage
{
public:
void head()
{
cout << "首页、公开课...." << endl;
}
void foot()
{
cout << "帮助中心、交流合作..." << endl;
}
void left()
{
cout << "java、c++...." << endl;
}
};
class java :public basepage
{
public:
void content()
{
cout << "java视频" << endl;
}
};
class python :public basepage
{
public:
void content()
{
cout << "python视频" << endl;
}
};
class cpp :public basepage
{
public:
void content()
{
cout << "c++视频" << endl;
}
};
void test()
{
cout << "————————————" << endl;
cout << "java页面如下:" << endl;
java j;
j.head();
j.foot();
j.left();
j.content();
cout << "————————————" << endl;
cout << "python页面如下:" << endl;
python p;
p.head();
p.foot();
p.left();
p.content();
cout << "————————————" << endl;
cout << "cpp页面如下:" << endl;
cpp c;
c.head();
c.foot();
c.left();
c.content();
}
int main()
{
test();
system("pause");
return 0;
}
继承方式
语法:class 子类 :继承方式 父类
继承方式分为: public 公开继承
protect 保护继承
private 私有继承
继承中的对象模型
父类中的所有成员都会被子类继承下来,其中私有属性被编译器隐藏起来,因此访问不到
继承中构造和析构顺序
子类继承父类后,创建子类对象,也会调用父类的构造函数
继承中构造与析构的数据: 先构造父类,再构造子类,析构的顺序与构造相反
继承同名成员处理方式
访问子类同名数据,直接访问
访问父类同名数据,需要加作用域
如果子类中出现了与父类中同名的成员函数,编译器会自动隐藏掉父类中所有的同名成员函数
#include<iostream>
using namespace std;
class person
{
public:
int m_age;
person()
{
m_age = 1;
}
void func()
{
cout << "父类成员函数" << endl;
}
};
class son :public person
{
public:
int m_age;
son()
{
m_age = 2;
}
void func()
{
cout << "子类成员函数" << endl;
}
};
//同名属性
void test()
{
son s;
cout << s.m_age << endl;
//通过子类对象访问父类中的属性,需要加作用域
cout << s.person::m_age << endl;
}
//同名函数
void test1()
{
son s1;
s1.func();
s1.person::func();
}
int main()
{
test();
test1();
system("pause");
return 0;
}
继承同名静态成员处理方式
访问子类同名成员,直接访问
访问父类同名成员,需要作用域
#include<iostream>
using namespace std;
//静态成员变量 所有对象共享一份数据,在编译阶段分配内存,类内申明,类外初始化
class person
{
public:
static int m_a;
static void func()
{
cout << "父类成员函数" << endl;
}
};
int person::m_a = 1;
class son :public person
{
public:
static int m_a;
static void func()
{
cout << "子类成员函数" << endl;
}
};
int son::m_a = 2;
//同名静态成员属性
void test()
{
son s;
//静态成员属性有两种访问方式
//1 通过对象
cout << s.m_a << endl;
cout << s.person::m_a << endl;
//2 通过类名
cout << son::m_a << endl;
cout << son::person::m_a << endl;
//同名静态成员函数
//1 通过对象
s.func();
s.person::func();
//2 通过类名
son::func();
son::person::func();
}
int main()
{
test();
system("pause");
return 0;
}
多继承语法
C++允许一个类继承多个类
语法:class 子类 :继承方式 父类1,继承方式 父类2...
多继承可能会导致父类中有同名成员出现,需要加作用域区分
#include<iostream>
using namespace std;
class person
{
public:
int m_a;
person()
{
m_a = 1;
}
};
class person1
{
public:
int m_b;
person1()
{
m_b = 2;
}
};
class son :public person, public person1
{
public:
int m_c, m_d;
son()
{
m_c = 3;
m_d = 4;
}
};
void test()
{
son s;
cout << s.m_c << " " << s.m_d << endl;
cout << s.person::m_a << endl;
cout << s.person1::m_b << endl;
}
int main()
{
test();
system("pause");
return 0;
}
菱形继承
概念:两个派生类继承一个基类,又有某个类同时继承这两个派生类
#include<iostream>
using namespace std;
class animal
{
public:
int m_age;
};
//利用虚继承解决菱形继承的问题 在继承之前加关键字 virtual
//animal类称为虚基类
class sheep :virtual public animal
{
};
class tuo :virtual public animal
{
};
class stuo :public sheep, public tuo
{
};
void test()
{
stuo st;
st.sheep::m_age = 1;
st.tuo::m_age = 2;
//当菱形继承,两个父类具有相同的数据,需要加作用域才能区分
//菱形继承导致数据有两份,资源浪费
cout << st.sheep::m_age << endl;
cout << st.tuo::m_age << endl;
cout << st.m_age << endl;
}
int main()
{
test();
system("pause");
return 0;
}
多态
多态分类:
静态多态:函数重载和运算符重载属于静态多态,复用函数名
动态多态:派生类和虚函数实现运行多态
静态多态和动态多态区别;
静态多态的函数地址早绑定 编译阶段确定函数地址
动态多态的函数地址晚绑定 运行阶段确定函数地址
(不管什么类型的指针都占4字节)
#include<iostream>
using namespace std;
class animal
{
public:
//虚函数
virtual void speak()
{
cout << "动物在说话" << endl;
}
};
class cat :public animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
};
class dog :public animal
{
public:
void speak()
{
cout << "小狗在说话" << endl;
}
};
//地址早绑定,在编译阶段确定函数地址
//动态多态满座条件: 1、有继承关系 2、子类重写父类的虚函数
//重写: 函数的返回值类型,参数李彪,函数名完全相同
//动态多态的使用: 父类的指针或引用,执行子类对象
void dospeak(animal& animal) //animal &animal = cat; 引用
{
animal.speak(); //当变为虚函数后,此函数地址不能被提前确定下来,需要先看下面的传入什么数据
}
void test()
{
cat c;
dospeak(c);
dog d;
dospeak(d);
}
int main()
{
test();
system("pause");
return 0;
}
多态优点:
- 代码组织结构清晰
- 可读性强
- 利于前期、后期的扩展维护
案例 计算器类
分别利用 普通写法 和 多态技术 设计实现两个操作数进行运算的计算器类
#include<iostream>
#include<string>
using namespace std;
//普通写法
class calculator
{
public:
int m_num1;
int m_num2;
int getresylt(string oper)
{
if (oper == "+")
{
return m_num1 + m_num2;
}
else if (oper == "-")
{
if (m_num1 > m_num2)
{
return m_num1 - m_num2;
}
else
return m_num2 - m_num1;
}
else if (oper == "*")
{
return m_num1 * m_num2;
}
}
};
void test()
{
calculator c;
c.m_num1 = 4;
c.m_num2 = 2;
cout << c.m_num1 << "-" << c.m_num2 << "=" << c.getresylt("-") << endl;
}
//利用多态实现计算器
//实现计算器抽象函数
class calculator1
{
public:
int m_num1;
int m_num2;
virtual int getresult() = 0
{
return 0;
}
};
//加法
class add :public calculator1
{
public:
int getresult()
{
return m_num1 + m_num2;
}
};
//减法
class jian :public calculator1
{
public:
int getresult()
{
return m_num1 - m_num2;
}
};
//乘法
class chen :public calculator1
{
public:
int getresult()
{
return m_num1 * m_num2;
}
};
void test1()
{
calculator1* a = new add;
a->m_num1 = 21;
a->m_num2 = 12;
cout << a->getresult() << endl;
delete a; //用完后销毁
calculator1* j = new jian;
j->m_num1 = 21;
j->m_num2 = 12;
cout << j->getresult() << endl;
delete j;
calculator1* c = new chen;
c->m_num1 = 21;
c->m_num2 = 12;
cout << c->getresult() << endl;
delete c;
}
int main()
{
test();
test1();
system("pause");
return 0;
}
纯虚函数和抽象类
在多态中,通常父类中虚函数的实现是没有意义的,主要是调用子类中重写的内容,因此可以将虚函数改写为纯虚函数,当类中有了纯虚函数,这个类称为抽象类
纯虚函数语法:
virtual 返回值类型 函数名 (参数列表)=0;
抽象类特点:
无法实例化对象
子类必须重写抽象类中的纯虚函数,否则子类也为抽象类
#include<iostream>
using namespace std;
class base
{
public:
//纯虚函数
virtual void func() = 0;
};
class son :public base
{
public:
virtual void func()
{
cout << "1" << endl;
}
};
class son1 :public base
{
public:
virtual void func()
{
cout << "2" << endl;
}
};
void test()
{
//抽象类不允许实例化,无论开辟在堆区还是栈区
//new calculator;
//calculator c;
son s;
s.func();
son1 s1;
s1.func();
}
int main()
{
test();
system("pause");
return 0;
}
虚析构和纯虚析构
共性:
可以解决父类指针释放子类对象
都需要有具体的函数实现
区别:
如果是纯虚析构,该类为抽象类,无法实例化对象
虚析构语法: virtual ~类名(){}
纯虚析构语法:virtual ~类名()= 0;
#include<iostream>
#include<string>
using namespace std;
class animal
{
public:
animal()
{
cout << "animal构造函数调用" << endl;
}
//虚析构
//virtual ~animal()
//{
// cout << "animal析构函数调用" << endl;
//}
//纯虚析构需要声明,也休息需要实现
//有了纯虚析构后,这个类也属于抽象类,无法实例化对象
virtual ~animal() = 0;
//纯虚函数
virtual void speak() = 0;
};
animal::~animal()
{
cout << "纯虚析构" << endl;
}
class cat :public animal
{
public:
string* m_name;
cat()
{
cout << "cat构造函数调用" << endl;
}
cat(string name)
{
m_name = new string(name);
}
virtual void speak()
{
cout << *m_name << "小猫会说话" << endl;
}
~cat()
{
if (m_name != NULL)
{
cout << "cat析构函数调用" << endl;
delete m_name;
m_name = NULL;
}
}
};
void test()
{
animal* animal = new cat("Tom");
animal->speak();
//父类指针析构的时候 不会调用子类中析构函数,导致子类中如果有堆区属性,会出现内存泄漏
delete animal;
}
int main()
{
test();
system("pause");
return 0;
}
文件操作
程序运行时产生的数据都是临时数据,程序一旦运行结束就会被释放,通过文件可以将数据持久化
C++中对文件操作需要加头文件<fstream>
文件类型:
- 文本文件 文件以ASCII码形式存储于计算机中
- 二进制文件 文件以二进制形式存储于计算机中,用户一般不能直接读懂他
操作文件的三大类:
- ofstream :写操作
- ifstream :读操作
- fstream :读写操作
文本文件
写文件
- 包含头文件 #include<fstream>
- 创建流对象 ofstream ofs;
- 打开文件 ofs.open(“文件路径”,打开方式);
- 写数据 ofs<<”写入的数据”;
- 关闭文件 ofs.close();
注:文件打开方式可以配合使用,利用 | 操作符
读文件
读文件步骤:
- 包含头文件 include<fstream>
- 创建流对象 ifstream ifs;
- 打开文件并判断文件是否打开成功 ifs .open(“文件路径”,打开方式);
- 读数据 四种方式读取
- 关闭文件 ifs.close();
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
void test()
{
ifstream ifs;
ifs.open("test.txt", ios::in);
if (!ifs.is_open())
{
cout << "失败" << endl;
return;
}
//读数据
//第一种
//char buf[1024] = { 0 };
//while (ifs >> buf)
//{
// cout << buf << endl;
//}
//第二种
//char buf[1024] = { 0 };
//while (ifs.getline(buf, sizeof(buf)))
//{
// cout << buf << endl;
//}
//第三种
//string buf;
//while ( getline(ifs,buf))
//{
// cout << buf << endl;
//}
//第四种
char c;
while ((c = ifs.get()) != EOF) //EOF end of file
{
cout << c << endl;
}
ifs.close();
}
int main()
{
test();
system("pause");
return 0;
}
二进制文件
以二进制方式对文件进行读写操作
打开方式要指定为 ios:: binary
写文件
二进制方式写文件主要利用流对象调用成员函数write
函数原型:ostream& write(const char*buffer,int len);
buffer 指向内存中的一段存储空间
len是读写的字符数
步骤:
1. 包含头文件 #include<fstream>
2.创建输出流对象
ofstream ofs("person.txt", ios::out | ios::binary);
3.打开文件
ofs.open("person.txt", ios::out | ios::binary);
或者 person p = { "张三",18 };
4.写文件 ofs.write((const char*)&p, sizeof(p));
5.关闭文件
#include<iostream>
#include<fstream>
using namespace std;
class person
{
public:
char m_name[64];
int m_age;
};
void test()
{
ofstream ofs("person.txt", ios::out | ios::binary);
//打开文件
//ofs.open("person.txt", ios::out | ios::binary);
person p = { "张三",18 };
ofs.write((const char*)&p, sizeof(p));
ofs.close();
}
int main()
{
test();
system("pause");
return 0;
}
读文件
二进制方式写文件主要利用流对象调用成员函数write
函数原型:istream& read(char *buffer,int len);
buffer 指向内存中的一段存储空间
len是读写的字符数
- 包含头文件
- 创建输出流对象
- 打开文件
- 写文件
- 关闭文件
#include<iostream>
#include<fstream>
using namespace std;
class person
{
public:
char m_name[64];
int m_age;
};
void test()
{
ifstream ifs("person.txt", ios::in | ios::binary);
//打开文件 判断文件是否打开成功
if(!ifs.is_open())
{
cout << "失败" << endl;
return;
}
person p;
ifs.read((char*)&p, sizeof(person));
cout << "姓名:" << p.m_name << " 年龄:" << p.m_age << endl;
ifs.close();
}
int main()
{
test();
system("pause");
return 0;
}
输出两位数字,当不足两位时,用0补齐
需要包含头文件 #include<iomanip>
{
int a;
cin >> a;
cout << setw(2) << setfill('0') << a << endl;
}