#include <iostream>
using namespace std;
int main()
{
//代码内容
//cout << "hello world" << endl;
system("pause");
return 0;
}
注释
作用:在代码中加一些说明和解释,方便自己或其他程序员阅读代码
方式一://单行注释
方式二:/*多行注释 */
变量
作用:给一段指定的内存空间起名,方便操作这段内存
存在意义:方便管理内存空间
语法:数据类型 变量名 = 初始值
#include <iostream>
using namespace std;
int main()
{
int a = 10;
cout << "a = " << a << endl;
system("pause");
return 0;
}
常量
作用:用于记录程序中不可更改的数据
C++定义常量的两种方法:
1、#define宏常量
语法:#define 变量名 常量值
通常在文件上方定义,表示一个常量
2、const修饰的变量
语法:const 数据类型 变量名 = 常量值
通常在变量定义前加关键字const,修饰该变量为常量,不可更改。
#include <iostream>
using namespace std;
//宏常量
#define day 7
int main()
{
//day = 8;
//错误,常量不可更改
cout << "一周有 " << day << "天" << endl;
const month = 12;
//month = 24;
//错误,常量不可更改
cout << "一年有 " << month << "个月份" << endl;
system("pause");
return 0;
}
关键字
作用:关键字是C++中预先保留的单词(标识符)
注意:在定义变量和常量的时候,不能使用关键字,否则会报错
标识符命名规则
作用:C++规定给标识符(常量、变量)命名时,有一套自己的规则
规则:
1、标识符不能是关键字;
2、标识符只能由字母、数字、下划线组成;
3、第一个字符必须为字母或下划线;
4、标识符中区分大小写。
数据类型
C++规定在创建一个变量或常量时,必须要指定出相应的数据类型,否则无法给变量分配内存
存在意义:给变量分配合适的空间
整型
作用:表示整数类型的数据
分类(所占空间不同):
short(短整型) 占用2字节空间
int (整型) 占用4字节空间
long(长整型) Windows占用4字节,Linux占用4(32位)或8(64位)字节
long long(长长整形) 占用8字节空间
#incude <iostream>
using namespace std;
#define day 7
int main()
{
short a = 5;
int b = 10;
long c = 15;
long long d = 20;
//区别于c语言的long long int a = 10;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
cout << "d = " << d << endl;
system("pause");
return 0;
}
sizeof关键字
作用:利用sizeof关键字可以统计数据类型所占内存的大小
语法:sizeof(数据类型/变量)
#incude <iostream>
using namespace std;
int main()
{
cout << "int类型所占内存空间为:" << sizeof(int) << endl;
system("pause");
return 0;
}
整型大小比较:short <int <=long <=long long
实型(浮点型)
作用:用于表示小数
分类:(表示的有效数字范围不同)
单精度float 占用4字节 7位有效数字
双精度double 占用8字节 15~16位有效数字
#include <iostream>
using namespace std;
int main()
{
float a = 3.14;
float b = 3.14f;
//在数字后加f可以减少程序转换的过程
double c = 3e2;
//科学计数法,表示3*10的平方
double d = 3e-2;
//科学计数法,表示3*0.1的平方
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
cout << "d = " << d << endl;
system("pause");
return 0;
}
字符型
作用:用于显示单个字符
语法:char ch = ‘a’;
注意:
1、在现实字符型变量时,用单引号将字符括起来,而不是双引号;
2、单引号内只能有一个字符,不可以是字符串。
C和C++中字符串变量都只占用1个字节
字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码[a 97, A 65]放入存储单元。
#include <iostream>
using namespace std;
int main()
{
char ch = 'a';
cout << ch << endl;
ch = 97;
//可以直接用ASCII编码给字符型变量赋值
cout << ch << endl;
system("pause");
return 0;
}
转义字符
作用:用于表示一些不能显示出来的ASCII字符。
转义字符 | 含义
\a | 报警
\b | 退格,将当前位置移到前一列
\f | 换页,将当前位置移到下页开头
\n | 换行,将当前位置移到下一行开头
\r | 回车,将当前位置移到本行开头
\t | 水平制表(跳到下一个TAB位置)一个\t占8个位置
\v | 垂直制表
\\ | 表示反斜线
\’ | 表示单引号字符
\? | 表示一个?
#include <iostream>
using namespace std;
int main()
{
cout << "aaa\thelloworld" <<endl;
//水平制表符作用:整齐输出内容
cout << "aaaa\thelloworld" <<endl;
system("pause");
return 0;
}
结果:
aaa helloworld
aaaa helloworld
字符串型
作用:用于表示一串字符
两种风格:
1、C风格字符串
语法:char 变量名[] = “字符串值”
注意:字符串要用双引号括起来
2、C++风格字符串
语法:string 变量名 = “字符串值”
注意:要包含头文件#include
#include <iostream>
using namespace std;
#include <string>
int main()
{
char str1[] = "hello";
cout << str1 << endl;
string str2[] = "world";
cout << str2 << endl;
system("pause");
return 0;
}
布尔类型 bool
作用:布尔数据类型代表真或假的值
bool类型只有两个值:
1、true -真(本质是1)
2、false -假(本质是0)
bool类型占据1个字节大小
#include <iostream>
using namespace std;
int main()
{
bool flag = true;
cout << flag <<endl;//1
//只要是非0的值则flag的值就为真
flag = false;
cout << flag <<endl;//0
system("pause");
return 0;
}
数据的输入
作用:用于从键盘获取数据
关键字:cin
语法:cin >> 变量
#include <iostream>
using namespace std;
#include <string>
int main()
{
int a = 0;
cout << "请给整型变量a赋值:" <<endl;
cin >> a;
cout << "a = " << a << endl;
float b;
cout << "请给浮点型变量f赋值:" <<endl;
cin >> f;
cout << "f = " << f << endl;
char ch;
cout << "请给字符型变量ch赋值:" <<endl;
cin >> ch;
cout << "ch = " << ch << endl;
string str = "hello";
cout << "请给字符串str赋值:" <<endl;
cin >> str;
cout << "str = " << str << endl;
bool flag = false;
cout << "请给布尔类型flag赋值:" <<endl;
cin >> flag;
cout << "flag = " << flag << endl;
system("pause");
return 0;
}
运算符
作用:用于执行代码的运算
算数运算符(同C语言)
作用:用于处理四则运算
包括:
+(正号)、-(负号)、+、-、*、
/、%、(除数不能为0,且两个小数不能进行取模运算)
++(前置递增)、(先加1,后运算)
++(后置递增)、(先运算,后加1)
–(前置递减)、–(后置递减)
赋值运算符(同C语言)
作用:用于将表达式的值赋给变量
包括:
=、+=、-=、*=、/=、%=
比较运算符(同C语言)
作用:用于表达式的比较
包括:
==、!=、<=、>=、<、>
逻辑运算符
作用:用于根据表达式的值返回真值或假值
包括:
!(非)、[a为真,则!a为假]
&&(与)、[一假则假]
||(或)[一真则真]
程序流程结构
C/C++支持的最基本的三种程序运行结构:
顺序结构:程序按顺序执行,不发生跳转
选择结构:依据条件是否满足,有选择的执行相应功能
顺序结构:依据条件是否满足,循环多次执行某段代码
选择结构
if语句
作用:执行满足条件的语句
三种形式:
1、单行合适if语句
语法 :if (条件) {条件满足执行的语句}
#include <iostream>
using namespace std;
int main ()
{
int score = 0;
cout << "请输入分数:" << endl;
cin >> score;
cout << "您输入的分数为:" << score << endl;
if (score > 550)//注意:这里不能加分号
{
cout << "恭喜您考上了一本大学" << endl;
}
system("pause");
return 0;
}
2、多行格式if语句
语法:if(条件) {条件满足执行的语句} else {条件不满足执行的语句};
#include <iostream>
using namespace std;
int main ()
{
int score = 0;
cout << "请输入分数:" << endl;
cin >> score;
cout << "您输入的分数为:" << score << endl;
if (score > 550)//注意:这里不能加分号
{
cout << "恭喜您考上了一本大学" << endl;
}
else
{
cout << "未考上一本大学" << endl;
}
system("pause");
return 0;
}
3、多条件的if语句
语法:if(条件1){条件一满足执行的语句} else if (条件2){条件2满足执行的语句}…else {都不满足执行的语句}
#include <iostream>
using namespace std;
int main ()
{
int score = 0;
cout << "请输入分数:" << endl;
cin >> score;
cout << "您输入的分数为:" << score << endl;
if (score > 550)//注意:这里不能加分号
{
cout << "恭喜您考上了一本大学" << endl;
}
else if (score > 450)
{
cout << "恭喜您考上了二本大学" << endl;
}
else if (score > 350)
{
cout << "恭喜您考上了三本大学" << endl;
}
else
{
cout << "未考上本科大学" << endl;
}
system("pause");
return 0;
}
嵌套if语句
#include <iostream>
using namespace std;
int main ()
{
int score = 0;
cout << "请输入分数:" << endl;
cin >> score;
cout << "您输入的分数为:" << score << endl;
if (score > 550)//注意:这里不能加分号
{
if (score > 700)
{
cout << "恭喜您考上了北京大学" << endl;
}
else if (score > 650)
{
cout << "恭喜您考上了清华大学" << endl;
}
else
{
cout << "恭喜您考上了一本大学" << endl;
}
}
else if (score > 450)
{
cout << "恭喜您考上了二本大学" << endl;
}
else if (score > 350)
{
cout << "恭喜您考上了三本大学" << endl;
}
else
{
cout << "未考上本科大学" << endl;
}
system("pause");
return 0;
}
三目运算符
作用:通过三目运算符实现简单的判断
语法:表达式1 ?表达式2 : 表达式3
解释:
如果表达式1的值为真,执行表达式2,并返回表达式2的结果;
如果表达式1的值为假,执行表达式3,并返回表达式3的结果。
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = (a > b ? a : c);
//将a和b作比较,并将较大的值赋给变量c
cout << "c = " << c << endl;
//在C++中三目运算符返回的是变量,可以继续赋值
(a > b ? a : b) = 100;
cout << "a = " << a << endl;//10
cout << "b = " << b << endl;//100
system("pause");
return 0;
}
switch语句
作用:执行多条件分支语句
语法:
switch (表达式)
{
case 结果1 :执行语句;break;
case 结果2 :执行语句;break;
...
default:执行语句;break;
}
#include <iostream>
using namespace std;
int main()
{
int score = 0;
cout << "请给电影打分:" << endl;
cin >> score;
cout << "您打的分数是:" << 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;
system("pause");
return 0;
}
if和switch的区别:
switch判断类型只能是整型或者字符型,不可以是一个区间
case里如果没有break,那么程序会一直向下执行
switch的结构清晰,执行效率高
循环结构
while语句
作用:满足循环条件,执行循环语句
语法:while (循环条件) {循环语句}
解释:只要循环条件的结果为真,就执行循环语句
#include <iostream>
using namespace std;
int main()
{
int num = 0;
//要避免死循环的条件
while (num < 10)
{
cout << num << endl;
num++;
}//从数字0打印到数字9
system("pause");
return 0;
}
生成随机数
int num = rand()%100 + 1//生成1~100的随机数 伪随机数
int num2 = srand((unsigned int)time(NULL));//随机数种子
可利用break来退出循环
do while循环语句
作用:满足循环条件,执行循环语句
语法:do {循环语句} while (循环条件)
注意:与while的区别在于do…while会先执行一次循环语句,再判断循环条件
#include <iostream>
using namespace std;
int main()
{
int num = 0;
do{
cout << num << endl;
num++;
}while (num < 10);//从数字0打印到数字9
//先执行一次循环语句再循环
system("pause");
return 0;
}
for循环语句
作用:满足循环条件,执行循环语句
语法:for(起始表达式;条件表达式;末尾循环体){循环语句}
#include <iostream>
using namespace std;
int main()
{
//从数字0打印到数字9
for (int i = 0;i < 10;i++)
{
cout << i << endl;
}
system("pause");
return 0;
}
注意:for循环的表达式,要用分号来进行分割
嵌套循环
作用:在循环体中再嵌套一层循环,解决一些实际问题
#include <iostream>
using namespace std;
int main()
{
//从数字0打印到数字9
for (int i = 0;i < 10;i++)//外层循环
{
for (int j = 0;j < 10;j++)//内层循环
{
cout << "* " ;
}
cout << endl;
}
system("pause");
return 0;
}
跳转语句
break语句
作用:用于跳出选择结构或者循环结构
break使用的时机:
1、出现在switch条件语句中,作用是终止case并跳出switch;
2、出现在循环语句中,作用是跳出当前的循环语句;
3、出现在嵌套循环中,跳出最近的内层循环语句。
#include <iostream>
using namespace std;
int main()
{
//switch语句中
cout << "请选择副本难度" << endl;
cout << "1、普通" << endl;
cout << "2、中等" << endl;
cout << "3、困难" << endl;
int select = 0;
cin >> select;
switch(select)
{
case 1:
cout << "您选择的是普通难度" << endl;
break;//退出switch语句
case 2:
cout << "您选择的是中等难度" << endl;
break;
case 3:
cout << "您选择的是困难难度" << endl;
break;
default:
break;
}
//循环语句中
for (int i = 0; i < 10;i++)
{
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()
{
for (int i = 0; i <= 100; i++)
{
//如果是奇数输出,偶数不输出
if (i % 2 == 0)
{
continue;//break会退出循环,而continue不会
}
cout << i << endl;
}
system("pause");
return 0;
}
goto语句
不推荐使用
作用:可以无条件跳转语句
语法:goto 标记;
解释:如果标记的名称存在,执行到goto语句时,会跳转到标记的位置
#include <iostream>
using namespace std;
int main()
{
cout << "1" << endl;
cout << "2" << endl;
goto FLAG;
cout << "3" << endl;
cout << "4" << endl;
FLAG://若不存在标记程序会报错
cout << "5" << endl;
system("pause");
return 0;
}
数组
所谓数组,就是一个集合,里面存放了相同元素的数据类型
特点:
1、数组中的每个数据元素都是相同的数据类型;
2、数组是由连续的内存位置组成的。
一维数组的定义方式
一维数组的定义方式:
1、数据类型 数组名【 数组长度】
2、数据类型 数组名【 数组长度】 = { 值1,值2 …};
3、数据类型 数组名 【】= { 值1,值2 …};
注意:
1、数组名的命名规范与变量名命名规范一致,不要和变量名重名
2、数组中下标是从0开始的
#include <iostream>
using namespace std;
int main()
{
int arr1[5];
arr1[0] = 10;
arr1[1] = 20;
arr1[2] = 30;
arr1[3] = 40;
arr1[4] = 50;
for (int i = 0; i < 5; i++)
{
cout << arr1[i] << endl;
}//利用循环输出数组中的元素
int arr2[5] = {11,12,13,14,15};
//若在初始化时没有全部填写完,会用0来代替
for (int i = 0; i < 5; i++)
{
cout << arr2[i] << endl;
}
int arr3[] = {21,22,23,24,25,26,27,28};
for (int i = 0; i < 8; i++)
{
cout << arr2[i] << endl;
}
system("pause");
return 0;
}
一维数组数组名
一维数组名称的用途:
1、可以统计整个数组在内存中的长度;
2、可以获取数组在内存中的首地址。
#include <iostream>
using namespace std;
int main()
{
int arr[5] = {1,2,3,4,5};
int a = sizeof(arr);//统计整个数组占用内存大小
int b = sizeof(arr[0]);//统计数组中一个元素占用内存大小
cout << "数组arr占用内存为:" << a << endl;
cout << "数组arr中第一个元素占用内存为:" << b << endl;
cout << "数组arr中元素个数为:" << a/b << endl;
cout << "数组的首地址为:" << arr << endl;//输出十六进制下的数组首地址
//vc中
//cout << "数组的首地址为:" << (int)arr << endl;//输出十进制下的数组首地址
//cout << "数组的第一个元素的地址为:" << (int)&arr[0] << endl;
//cout << "数组的第二个元素的地址为:" << (int)&arr[1] << endl;//与上一个差b个字节
//arr = 100;
//错误,数组名是常量,不可以进行赋值操作
system("pause");
return 0;
}
冒泡排序
作用:最常用的排序算法,对数组内元素进行排序
步骤:
1、比较相邻的元素。如果第一个比第二个大,就交换它们两个;
2、对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值
3、重复以上的步骤,每次比较次数-1,直到不需要比较。
#include <iostream>
using namespace std;
int main()
{
int arr[9] = {4,2,8,0,5,7,1,3,9};
cout << "排序前:" << endl;
for (int i = 0;i < 9;i++)
{
cout << arr[i] << "";
}
cout << endl;
//冒泡排序
for (int i = 0; i < 8; i++)
{
//内层循环对比
for (int j = 0; j < 8 - i; j++)
{
if (arr[j] > arr[j+1])
{
int temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
cout << "排序后:" << endl;
for (int i = 0;i < 9;i++)
{
cout << arr[i] << "";
}
cout << endl;
system("pause");
return 0;
}
二维数组(在一维数组上多加一个维度)
二维数组定义方式:
1、数据类型 数组名 [ 行数 ] [ 列数 ];
2、数据类型 数组名 [ 行数 ] [ 列数 ] = { {数据1,数据2},{数据3,数据4} };
3、数据类型 数组名 [ 行数 ] [ 列数 ] = { 数据1,数据2,数据3,数据4 };
4、数据类型 数组名 [ ] [ 列数 ] = { 数据1,数据2,数据3,数据4 };
建议:第二种更直观,提高代码的可读性
#include <iostream>
using namespace std;
int main() {
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;
//外层循环打印行数,内层循环打印列数
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
cout << arr[i][j] << " ";
}
cout << endl;
}
int arr2[2][3] = { {1,2,3},{4,5,6} };
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
cout << arr2[i][j] << " ";
}
cout << endl;
}
int arr3[2][3] = { 1,2,3,4,5,6 };
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
cout << arr3[i][j] << " ";
}
cout << endl;
}
int arr4[][3] = {1, 2, 3, 4, 5, 6};
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
cout << arr4[i][j] << " ";
}
cout << endl;
}
system("pause");
return 0;
}
二维数组数组名
作用:
1、查看二维数组所占内存空间
2、获取二维数组首地址
#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 << "二维数组的首地址为:" << (int)arr << endl;
cout << "二维数组第一行的首地址为:" << (int)arr[0] << endl;
cout << "二维数组第一个元素的地址为:" << (int)&arr[0][0] << endl;
cout << "二维数组第二行的首地址为:" << (int)arr[1] << endl;
//与第一行差sizeof(arr[0])个字节
system("pause");
return 0;
}
函数
作用:将一段经常使用的代码封装起来,减少重复代码
一个较大的程序,一般分为若干个程序块,每个模块实现特定的功能
函数的定义
定义步骤:
1、返回值类型
2、函数名
3、参数列表(形参)
4、函数体语句
5、return表达式
语法:
返回值类型 函数名 (参数列表)
{
函数体语句
return 表达式
}
定义示例
int add(int num1, int num2)
{
int sum;
sum = num1 + num2;
return sum;
}
函数的调用
功能:使用定义好的函数
语法:函数名(实际参数)
#include <iostream>
using namespace std;
int add(int num1, int num2)
{
int sum;
sum = num1 + num2;
return sum;
}
int main() {
int a = 10;
int b = 20;
int c;
c = add(a, b);
cout << "和为:" << 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;//不需要返回值时,可以不写return语句
}
int main() {
int a = 10;
int b = 20;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
//值传递时,函数的形参发生改变并不会影响实参
swap(a, b);
cout << "a = " << a << endl;//并未改变
cout << "b = " << b << endl;
system("pause");
return 0;
}
函数常见样式
1、无参无返
2、有参无返
3、无参有返
4、有参有返
#include <iostream>
using namespace std;
//无参无返
void test01( )
{
cout << "this is test01" << endl;
}
//有参无返
void test02(int a)
{
cout << "this is test02 a = " << a << endl;
}
//无参有返
int test03()
{
cout << "this is test03" << endl;
return 1000;
}
//有参有返
int test04(int a)
{
cout << "this is test04 a = " << a << endl;
return a;
}
int main() {
test01();
test02(100);
int a = test03();
cout << "a = " << a << endl;
int b = test04(10000);
cout << "b = " << b << endl;
system("pause");
return 0;
}
函数的声明
作用:告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义
注函数的声明可以多次,但是函数的定义只能有一次
#include <iostream>
using namespace std;
int max(int a, int b);//函数的声明
//提前告诉编译器函数的存在
int main() {
int a = 10;
int b = 20;
cout << max(a, b) << endl;
system("pause");
return 0;
}
//有函数声明时,函数定义可以放在main函数后面,否则会报错
int max(int a, int b)
{
return a > b ? a : b;
}
函数的分文件编写
作用:让代码结构更加清晰
原文件:
#include <iostream>
using namespace std;
void swap(int a, int b)//不需要返回值
{
int temp = a;
a = b;
b = temp;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return;//不需要返回值时,可以不写return语句
}
int main() {
int a = 10;
int b = 20;
swap(a, b);
system("pause");
return 0;
}
编写步骤:
1、创建后缀名为.h的头文件
2、创建后缀名为.cpp的源文件
3、在头文件中写函数的声明
4、在源文件中写函数的定义
调用:
指针
指针的基本概念
指针的作用:可以通过指针间接访问内存
内存编号是从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 = 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;
p = &a;
cout << "sizeof(int *) = " << sizeof(p) << endl;
cout << "sizeof(float *) = " << sizeof(float *) << endl;
cout << "sizeof(double *) = " << sizeof(double *) << endl;
cout << "sizeof(char *) = " << sizeof(char *) << endl;
system("pause");
return 0;
}
空指针
定义:指针变量指向内存中编号为0的空间
作用:初始化指针变量
注意:空指针指向的内存是不可以访问的
原因:0~255之间的内存编号是系统占用的,因此不可以访问
#include <iostream>
using namespace std;
int main() {
//空指针用于给指针变量进行初始化
int * p = NULL;
//*p = 100;
//错误,空指针是不可以访问的
system("pause");
return 0;
}
野指针
定义:指针变量指向非法的内存空间
#include <iostream>
using namespace std;
int main() {
//野指针
int * p = (int *)0x1100;
//异常,读取访问程序出错
//访问野指针报错
cout << *p << endl;
system("pause");
return 0;
}
const修饰指针
const修饰指针的三种情况:
1、const修饰指针 ——— 常量指针[const int * p = &a]
特点:指针的指向可以修改,但指针指向的值不可以修改
2、const修饰常量 ———指针常量[int * cont p = &a]
特点:指针的指向不可以修改,但指针指向的值可以修改
3、const既修饰指针,又修饰常量[const int *const p = &a]
特点:指针的指向和指针指向的值都不可以修改
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 10;
const int * p = &a;
//*p = 20;错误
p = &b;//正确
int * const p2 = &a;
*p2 = 20;//正确
//p2 = &b;错误
const int * const p3 = &a;
//*p3 = 100;错误
//p3 = &b;错误
system("pause");
return 0;
}
指针和数组
作用:利用指针访问数组中元素
#include <iostream>
using namespace std;
int main() {
int arr[] = { 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;
int *p2 = arr;
cout << "利用指针遍历数组" << endl;
for (int i = 0; i < 10; i++)
{
cout << *p2 << endl;
p2++;
}
system("pause");
return 0;
}
指针和函数
作用:利用指针作为函数参数,可以修改实参的值
#include <iostream>
using namespace std;
void swap01(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "swap01 a = " << a << endl;
cout << "swap01 b = " << b << endl;
}
void swap02(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
cout << "swap02 a = " << *p1 << endl;
cout << "swap02 b = " << *p2 << endl;
}
int main() {
//值传递
int a = 10;
int b = 20;
swap01(a, b);
//值传递不会修改实参的值
cout << "a = " << a << endl;
cout << "b = " << b << endl;
//地址传递
swap02(&a, &b);
//地址传递可以修改实参,更改了指针指向的值
cout << "a = " << a << endl;
cout << "b = " << b << endl;
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 temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//打印数组
void printArry(int *arr, int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << endl;
}
}
int main() {
//创建数组
int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };
int len = sizeof(arr) / sizeof(arr[0]);
//创建函数,实现冒泡排序
bubbleSort(arr, len);
//打印排序后的数组
printArry(arr, len);
system("pause");
return 0;
}
结构体
结构体的基本概念
结构体数与用户自定义的数据类型,允许用户存储不同的数据类型
结构体的定义和使用
语法:struct 结构体名 { 结构体成员列表 };
通过结构体创建变量的方式:
1、struct 结构体名 变量名
2、struct 结构体名 变量名 = { 成员1值,成员2值…}
3、定义结构体时顺便创建变量不建议使用
#include <iostream>
using namespace std;
#include <string>
//创建学生数据类型 :学生包括:姓名,年龄,分数
//结构体定义时,struct关键字不可以省略
struct Student
{
//姓名
string name;
//年龄
int age;
//分数
int score;
}s3;
//法三
int main() {
//通过学生类型创建具体学生
//法一
struct Student s1;
//结构体变量创建时,struct关键字可以省略
//Student s1;
//给s1属性赋值
s1.name = "张三";
s1.age = 18;
s1.score = 100;
cout << "姓名:" << s1.name << "年龄:" << s1.age
<< "分数: " << s1.score << endl;
//法二
struct Student s2 = { "李四",19,80 };
cout << "姓名:" << s2.name << "年龄:"
<< s2.age << "分数: " << s2.score << endl;
//法三,在定义结构体时创建结构体变量
s3.name = "王五";
s3.age = 20;
s3.score = 60;
cout << "姓名:" << s3.name << "年龄:" << s3.age
<< "分数: " << s3.score << endl;
system("pause");
return 0;
}
结构体数组
作用:将自定义的结构体放入到数组中方便维护
语法:struct 结构体名 数组名[元素个数] = { {}, {} ,…{} }
#include <iostream>
using namespace std;
#include <string>
//定义结构体
struct Student
{
//姓名
string name;
//年龄
int age;
//分数
int score;
};
int main() {
//创建结构体数组并赋值
struct Student stuArray[3]=
{
{"张三",18,100},
{"李四",19,99},
{"王五",20,66}
};
//修改数值
stuArray[2].age = 19;
//遍历结构体数组
for (int i = 0; i < 3; i++)
{
cout << " 姓名: " << stuArray[i].name
<< " 年龄:" << stuArray[i].age
<< " 分数:" << stuArray[i].score << endl;
}
system("pause");
return 0;
}
结构体指针
作用:通过指针访问结构体中的成员
利用操作符->可以通过结构体指针访问结构体属性
#include <iostream>
using namespace std;
#include <string>
//定义学生结构体
struct Student
{
string name;//姓名
int age;//年龄
int score;//分数
};
int main() {
//创建学生结构体变量
struct Student s = { "张三",18,100 };//struct可以省略
//通过指针指向结构体变量
//int *p = &s;错误
struct Student * p = &s;//struct可以省略
//通过指针访问结构体变量中的数据
cout << " 姓名:" << p->name
<< " 年龄:" << p->age
<< " 分数:" << p->score << endl;
system("pause");
return 0;
}
结构体嵌套结构体
作用:结构体中的成员可以是另一个结构体
在结构体中定义另一个结构体作为成员,用以解决实际问题
#include <iostream>
using namespace std;
#include <string>
//定义学生结构体
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 = 10000;
t.name = "老王";
t.age = 50;
t.stu.name = "小王";
t.stu.age = 20;
t.stu.score = 60;
cout << " 老师姓名:" << t.name
<< " 老师标号:" << t.id
<< " 老师年龄:" << t.age
<< " 老师辅导的学生姓名:" << t.stu.name
<< " 老师辅导的学生年龄:" << t.stu.age
<< " 老师辅导的学生分数:" << t.stu.score << endl;
system("pause");
return 0;
}
结构体做函数参数
作用:将结构体作为参数向函数中传递
传递方式:
1、值传递
2、地址传递
#include <iostream>
using namespace std;
#include <string>
//定义学生结构体
struct Student
{
string name; //姓名
int age; //年龄
int score; //分数
};
//值传递
void printStudent1(struct Student s)
{
s.age = 100;
cout << "子函数1中 姓名:" << s.name
<< " 年龄:" << s.age << " 分数:" << s.score << endl;
}
//地址传递
void printStudent2(struct Student * p)
{
p->age = 100;
cout << "子函数2中 姓名:" << p->name
<< " 年龄:" << p->age << " 分数:" << p->score << endl;
}
int main() {
//创建结构体变量
Student s;
s.name = "张三";
s.age = 20;
s.score = 85;
cout << "main函数中 姓名:" << s.name
<< " 年龄:" << s.age << " 分数:" << s.score << endl;
printStudent1(s);
cout << "main函数中 姓名:" << s.name
<< " 年龄:" << s.age << " 分数:" << s.score << endl;//年龄未被改变
printStudent2(&s);
cout << "main函数中 姓名:" << s.name
<< " 年龄:" << s.age << " 分数:" << s.score << endl;//年龄被改变
system("pause");
return 0;
}
结构体中const使用场景
作用:用const来防止误操作
#include <iostream>
using namespace std;
#include <string>
struct Student //定义学生结构体
{
string name; //姓名
int age; //年龄
int score; //分数
};
//将函数中的形参改为指针,可以减少内存空间
void printStudents(const Student * s)
{
//s->age = 150;
//错误,加入const后s不可修改,可防止误操作
cout << " 姓名:" << s->name << " 年龄:" << s->age
<< " 分数:" << s->score << endl;
}
int main() {
//创建结构体变量
struct Student s = { "张三",15,70 };
cout << "main函数中张三的年龄为:" << s.age << endl;
//通过函数来打印结构体变量信息
printStudents(&s);
cout << "main函数中张三的年龄为:" << s.age << endl;
system("pause");
return 0;
}
结构体案例1
#include <iostream>
using namespace std;
#include <string>//c++字符串需要的头文件
#include <ctime>//随机数种子需要的头文件
struct Student //定义学生结构体
{
string sName; //姓名
int score; //分数
};
struct Teacher //定义老师结构体
{
string tName; //姓名
struct Student sArray[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];
//给老师带的学生赋值
for (int j = 0; j < 5; j++)
{
tArray[i].sArray[j].sName = "Student_";
tArray[i].sArray[j].sName += nameSeed[j];
int random = rand() % 61 + 40;//40~100分区间内的随机数
tArray[i].sArray[j].score = random;
}
}
}
void printInfo(struct Teacher tArray[],int len)
{
for (int i = 0; i < len; i++)
{
cout << "老师姓名:" << tArray[i].tName << endl;
for (int j = 0; j < 5; j++)
{
cout << "\t学生姓名:" << tArray[i].sArray[j].sName
<< " 考试分数:" << tArray[i].sArray[j].score << endl;
}
}
}
int main() {
//随机数种子
srand((unsigned int)time(NULL));
//创建老师数组
Teacher tArray[3];
//通过函数给老师和学生信息赋值
int len = sizeof(tArray) / sizeof(tArray[0]);
allocateSpace(tArray, 3);
//打印所有老师及学生信息
printInfo(tArray, 3);
system("pause");
return 0;
}
结构体案例2
#include <iostream>
using namespace std;
#include <string>
//定义英雄结构体
struct Hero
{
string name;//姓名
int age;//年龄
string sex;//性别
};
//冒泡排序
void bubbleSort(struct Hero heroArray[], int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (heroArray[j].age > heroArray[j + 1].age)
{
struct Hero temp = heroArray[j];
heroArray[j] = heroArray[j + 1];
heroArray[j + 1] = temp;
}
}
}
}
//打印排序后数组中的信息
void printHero(struct Hero heroArray[], int len)
{
for (int i = 0; i < len; i++)
{
cout << "姓名:" << heroArray[i].name << " 年龄:" << heroArray[i].age
<< "性别" << heroArray[i].sex << endl;
}
}
int main() {
//创建数组并存放无名英雄
Hero heroArray[5] =
{
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"女"}
};
int len = sizeof(heroArray) / sizeof(heroArray[0]);
cout << "排序前:" << endl;
printHero(heroArray, len);
//对数组进行排序,按照年龄升序排序
bubbleSort(heroArray, len);
//将排序后的结果打印输出
cout << "排序后:" << endl;
printHero(heroArray, len);
system("pause");
return 0;
}
内存分区模型
C++在执行时,将内存大方向划分为4个区域:
- 代码区:存放函数体的二进制代码,由操作系统进行管理的;
- 全局区:存放全局变量和静态变量以及常量;
- 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等;
- 由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
内存四区的意义:
不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。
程序运行前
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域
代码区
- 存放CPU执行的机器指令
- 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
- 代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
全局区 - 全局变量和静态变量存放在此
- 全局区还包含了常量区,字符串常量和其他常量(const修饰的局部变量)也存放于此
- 该区域的数据在程序结束后由操作系统释放
#include <iostream>
using namespace std;
//全局变量
int g_a = 10;
int g_b = 10;
//const修饰的全局变量
const int c_g_a = 10;
const int c_g_b = 10;
int main() {
//创建普通局部变量
int a = 10;
int b = 10;
cout << "局部变量a的地址是:" << (int)&a << endl;
cout << "局部变量b的地址是:" << (int)&b << endl;
cout << "全局变量g_a的地址是:" << (int)&g_a << endl;
cout << "全局变量g_b的地址是:" << (int)&g_b << endl;
//静态变量 储存在全局区
static int s_a = 10;
static int s_b = 10;
cout << "静态变量s_a的地址是:" << (int)&s_a << endl;
cout << "静态变量s_b的地址是:" << (int)&s_b << endl;
//常量
//字符串常量
cout << "字符串常量的地址为:" << (int)&"hello world" << endl;
//const修饰的变量
//const修饰的全局变量
cout << "const修饰的全局变量s_a的地址是:" << (int)&c_g_a << endl;
cout << "const修饰的全局变量s_b的地址是:" << (int)&c_g_b << endl;
//const修饰的局部变量
const int c_l_a = 10;
const int c_l_b = 10;
cout << "const修饰的局部变量s_a的地址是:" << (int)&c_l_a << endl;
cout << "const修饰的局部变量s_b的地址是:" << (int)&c_l_b << endl;
system("pause");
return 0;
}
程序运行后
栈区
- 由编译器自动分配释放,存放函数的参数值、局部变量等
- 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
#include <iostream>
using namespace std;
//栈区数据注意事项:不要返回局部变量的地址
int * func(int b)//形参数据也会放在栈区
{
int a = 10;//局部变量,存放在栈区,栈区的数据在函数执行完后自动释放
int b = 100;//局部变量,存放在栈区,栈区的数据在函数执行完后自动释放
return &a;//返回局部变量的地址
}
int main() {
int * p = func(1);
cout << *p << endl;//第一次打印数据正确,因为编译器做了一次保留
cout << *p << endl;///第二次打印数据错误,编译器不再继续保留
system("pause");
return 0;
}
堆区
- 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
- 在C++中主要利用new在堆区开辟内存
#include <iostream>
using namespace std;
int * func( )
{
//利用new关键字将数据开辟到堆区
//指针本质也是局部变量,放在栈上,指针保存的数据是放在堆区
int * a = new int(10);
return a;
}
int main() {
int *p = func();
cout << *p << endl;
cout << *p << endl;//结果不变
system("pause");
return 0;
}
new运算符
- C++中利用new操作符在堆区开辟数据
- 堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete
- 语法:new 数据类型
- 利用new创建的数据,会返回该数据对应的类型的指针
#include <iostream>
using namespace std;
int * func()
{
//在堆区创建整型数据
//new返回的是 该数据类型的指针
int * p = new int(10);
return p;
}
void test01()
{
int * p = func();
cout << *p << endl;
cout << *p << endl;//堆区未释放,数据未丢失
//释放堆区数据
delete p;
//cout << *p << endl;//堆区被释放,报错
}
//在堆区利用new开辟数组
void test02()
{
//创建10个整型的数组,在堆区
int* arr =new int[10];//10代表数组有10个元素
//给数组赋值
for (int i = 0; i < 10; i++)
{
arr[i] = i + 100;
}
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 = 10;
//创建引用
int &b = a;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
b = 100;
cout << "a = " << a << endl;
cout << "b = " << b << endl;//操作a和b是操作的是同一块内存
system("pause");
return 0;
}
引用注意事项
- 引用必须要初始化
- 引用一旦初始化后,就不可以更改了
#include <iostream>
using namespace std;
int main() {
int a = 10;
//int &b;// 错误,必须要初始化
int &b = a;
int c = 20;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
b = c;//赋值操作,而不是更改引用
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl;
system("pause");
return 0;
}
引用做函数参数
- 作用:函数传递时,可以利用引用的技术让形参修饰实参
- 优点:可以简化指针修改实参
#include <iostream>
using namespace std;
//值传递
void mySwap01(int a,int b)
{
int temp = a;
a = b;
b = temp;
cout << "mySwap01中" << endl;
cout << "\ta = " << a << endl;
cout << "\tb = " << b << endl;
}
//地址传递
void mySwap02(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
//引用传递
void mySwap03(int &a, int &b)//等同于操作参数的别名
{
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10;
int b = 20;
cout << "初始值" << endl;
cout << "\ta = " << a << endl;
cout << "\tb = " << b << endl;
mySwap01(a, b);//值传递,形参不会修饰实参
cout << "mySwap01后" << endl;
cout << "\ta = " << a << endl;
cout << "\tb = " << b << endl;
mySwap02(&a, &b);//地址传递,形参会修饰实参的
cout << "mySwap02后" << endl;
cout << "\ta = " << a << endl;
cout << "\tb = " << b << endl;
mySwap03(a, b);//引用传递,形参也会修饰实参
cout << "mySwap03后" << endl;
cout << "\ta = " << a << endl;
cout << "\tb = " << 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 &ref = test01();
//不返回局部变量的引用
cout << "ref = " << ref << endl;//第一次结果正确
cout << "ref = " << ref << endl;//第二次结果错误
int &ref2 = test02();
cout << "ref2 = " << ref2 << endl;
cout << "ref2 = " << ref2 << endl;
test02() = 1000;//如果函数的返回值是引用,这个函数调用可以作为左值
cout << "ref2 = " << ref2 << endl;
cout << "ref2 = " << ref2 << endl;
system("pause");
return 0;
}
引用的本质
- 本质:引用在C++内部实现是一个指针常量
#include <iostream>
using namespace std;
//发现是引用,转换为 int* const ref = &a;
void func(int& ref)
{
ref = 100;//ref是引用,即*ref=100
}
int main() {
int a = 10;
//自动转换为 int* const ref = &a;指针常量是指针指向不可改,也说明为什么引用不可更改
int& ref = a;
ref = 20;//内部发现ref是引用,自动帮我们转换为: *ref = 20;
cout << "a = " << a << endl;
cout << "ref = " << ref << endl;
func(a);
cout << "a = " << a << endl;
cout << "ref = " << ref << endl;
system("pause");
return 0;
}
注意:C++推荐使用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
常量引用
- 作用:常量引用主要用来修饰形参,防止误操作
- 在函数形参列表中,可以加const修饰形参,防止形参改变实参
#include <iostream>
using namespace std;
void showValue(const int &val)//加const防止误操作
{
// val = 1000; // 错误 加上const后变为只读,不可以修改
cout << "val = " << val << endl;
}
int main() {
int a = 100;
//int &ref = 10; // 错误,引用必须引一块合法的内存空间
const int &ref = 10;//编译器将代码修改为 int temp =10;const int & ref = temp;
// ref = 20; //错误 加上const后变为只读,不可以修改
showValue(a);
cout << "a = " << a << endl;
system("pause");
return 0;
}
函数提高
函数默认参数
- 在C++中,函数的形参列表中的形参是可以有默认值的
- 语法:返回值类型 函数值 (参数 = 默认值)
#include <iostream>
using namespace std;
int func(int a, int b = 20, int c = 30)
{
return a + b + c;//若传了值,则用传的数据,否则就用默认值
}
//注意事项
//如果某个位置有了默认参数,那么从这个位置往后,从左到右都必须有默认值
//声明和实现只能有一个有默认参数
/*
int func2(int a = 10,int b = 10);
int func2(int a = 10,int b = 10) //错误,重定义默认参数
{
return a + b + c;
}
*/
int main() {
cout << func(10,30) << endl;
//cout << func2(10,20) << endl; //无法运行
system("pause");
return 0;
}
函数名占位参数(目前用不到)
- C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
- 语法:返回值类型 函数名(数据类型){}
#include <iostream>
using namespace std;
//占位参数,目前用不到
void func(int a,int)
{
cout << "this is func" << endl;
}
//占位参数可以有默认值
void func2(int a, int = 10)
{
cout << "this is func" << endl;
}
int main() {
func(10, 10);
//func(10)//错误,无法使用,占位参数必须填补
func2(10);
system("pause");
return 0;
}
函数重载概述
- 作用:函数名可以相同,提高复用性
- 函数重载满足条件
– 同一个作用域(全局域、局部域)下
– 函数名称相同
– 函数参数类型不同,或者个数不同,或者顺序不同 - 注意:函数的返回值不可以作为函数重载的条件
#include <iostream>
using namespace std;
void func()
{
cout << "func 的调用" << endl;
}
void func(int a)
{
cout << "func(int a) 的调用" << endl;
}
void func(double b)
{
cout << "func(double b) 的调用" << endl;
}
void func(int a,double b)
{
cout << "func(int a,double b) 的调用" << endl;
}
void func(double a, int b)
{
cout << "func(double a, int b) 的调用" << endl;
}
int main() {
func();//调用第一个
func(10);//调用第二个
func(3.14);//调用第三个
func(10, 3.14);//调用第四个
func(3.14, 10);//调用第五个
system("pause");
return 0;
}
函数重载的注意事项
- 引用作为重载条件
- 函数重载碰到函数默认参数
#include <iostream>
using namespace std;
//引用作为重载的条件
void func(int &a)
{
cout << "func(int &a)调用" << endl;
}
void func(const int &a)
{
cout << "func(const int &a)调用" << endl;
}
//函数重载碰到默认参数
void func2(int a)
{
cout << "func2(int a)调用" << endl;
}
void func2(int a,int b = 10)
{
cout << "func2(int a,int b)调用" << endl;
}
int main() {
int a = 10;
func(a);
func(10);//如果调用第一个则相当于int &a = 10 不合法,调用第二个函数,则是const int &a = 10; 合法
//func2(10);//当函数重载碰到默认参数,出现二义性,报错,尽量避免
func2(10, 20);//正确,只能调用有默认参数的函数
system("pause");
return 0;
}
类和对象
- C++面向对象的三大特征:
– 封装
– 继承
– 多态 - C++认为万事万物都皆为对象,对象上有其属性和行为
- 具有相同性质的对象,我们可以抽象称为类
封装
封装的意义
- 封装是C++面向对象三大特征之一
- 封装的意义:
– 1、将属性和行为作为一个整体,表现生活中的事物
– 2、将属性和行为加以权限控制
封装意义1 - 语法:class 类名 { 访问权限: 属性 / 行为 };
- 例:设计一个圆类,求圆的周长
#include <iostream>
using namespace std;
const double PI = 3.14;
class Circle
{
//访问权限
public: //公共权限
//属性
int m_r; //半径
//行为
double calculataZC() //获取圆的周长
{
return 2 * PI * m_r;
}
};
int main() {
//通过圆类,创建具体的圆(对象)——————实例化
Circle c1;
//给圆(对象)的属性赋值
c1.m_r = 10;
cout << "圆的周长为:" << c1.calculataZC() << endl;
system("pause");
return 0;
}
- 类中的属性和行为统称为成员
- 属性又叫做成员属性或成员变量
- 行为又叫做成员函数或成员方法
封装意义2
- 类在设计时,可以把属性和行为放在不同的权限下,加以控制
- 三种访问权限:
– 1、公共权限 public 成员 类内可以访问,类外可以访问
– 2、保护权限 protected 成员 类内可以访问,类外不可以访问
– 3、私有权限 private 成员 类内可以访问,类外不可以访问
#include <iostream>
using namespace std;
class Person
{
public: //公共权限
string m_Name;//名字
protected: //保护权限
string m_Car;//汽车
private: //私有权限
int m_Password;//银行卡密码
public://类内均可以访问
void func()
{
m_Name = "张三";
m_Car = "拖拉机";
m_Password = 12345;
}
};
int main() {
//实例化具体对象
Person p1;
p1.m_Name = "李四";
// p1.m_Car = "奔驰" //保护权限,类外无法访问
// p1.m_Password = 123; //私有权限,类外无法访问
system("pause");
return 0;
}
struct和class的区别
在C++中struct和class唯一的区别就在于默认访问权限不同:
- struct默认权限为公共
- class默认权限为私有
#include <iostream>
using namespace std;
class C1
{
int m_A;//默认权限为私有
};
struct C2
{
int m_A;//默认权限是公共
};
int main() {
C1 c1;
// c1.m_A = 100; //私有权限,类外不能访问
C2 c2;
c2.m_A = 100; //公共权限,类外可以访问
system("pause");
return 0;
}
成员属性设置为私有
优点:
- 1、将所有成员属性设置为私有,可以自己控制读写权限
- 2、对于写权限,我们可以检测数据的有效性
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
//写姓名
void setName(string name)
{
m_Name = name;
}
//读姓名
string getName()
{
return m_Name;
}
//获取年龄
int getAge()
{
return m_Age;
}
//修改年龄 年龄范围必须在0~150之间
void setAge(int age)
{
if (age < 0 || age > 150)
{
m_Age = 0;
cout << "您输入的年龄有误" << endl;
return;
}
else
m_Age = age;
}
//设置情人
void setLover(string lover)
{
m_Lover = lover;
}
private:
//姓名 可读可写
string m_Name;
//年龄 可读可写
int m_Age;
//情人 只写
string m_Lover;
};
int main() {
Person p;
// p.m_Name = "张三"; //私有权限,无法类外访问
p.setName("张三");
cout << "姓名为: " << p.getName() << endl;
// p.m_Age = 18; //私有权限,无法类外访问
p.setAge(1000);
cout << "年龄为: " << p.getAge() << endl;
p.setAge(18);
cout << "年龄为: " << p.getAge() << endl;
p.setLover("苍井");
//cout << "情人为: " << p.m_Lover() << endl; 无法访问
system("pause");
return 0;
}
对象的初始化和清理
- C++中的面向对象来源于生活,每个对象都有初始设置以及对象销毁前的清除数据的设置
构造函数和析构函数
对象的初始化和清理也是两个非常重要的安全问题:
- 一个对象或者变量没有初始状态,对其使用后果是未知
- 同样的使用完一个对象或变量,没有及时清理,也会造成移动的安全问题
C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工具 - 对象的初始化和清理工作是编译器强制要我们做的事情,因此我们不提供构造和析构,编译器会提供
- 编译器提供的构造函数和析构函数是空实现
构造函数:主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用
构造函数语法:类名(){}
- 1、构造函数,没有返回值也不写void
- 2、函数名称与类名相同
- 3、构造函数可以有参数,因此可以发生重载
- 4、程序在调用对象时会自动调用构造,无须手动调用,而且只会调用一次
析构函数:主要作用于对象销毁前系统自动调用,执行一些清理工作
析构函数语法:~类名(){}
- 1、析构函数,没有返回值也不写void
- 2、函数名称与类名相同,在名称前加上符号~
- 3、析构函数不可以有参数,因此不可以发生重载
- 4、程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
//构造函数
//创建对象的时候,会自动调用,且只调用一次
Person()
{
cout << "Person 构造函数的调用" << endl;
}
//析构函数
//对象在销毁前,会自动调用析构函数,而且指挥调用一次
~Person()
{
cout << "Person 析构函数的调用" << endl;
}
};
void test01()
{
Person p;//在栈上的数据,在test01执行完毕后,会自动释放这个对象
}
int main() {
test01();
Person p;//在main函数执行完时,这个对象才会被释放
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;
}
int age;
};
//调用
void test01()
{
Person p1;//默认构造函数调用 此处不加()
//Person p1();//若加,不调用class,因为编译器会认为这是一个函数的声明,不会认为在创建对象
//括号法
Person p2(10);//有参构造函数调用
Person p3(p2);//拷贝构造函数调用
cout << "p2的年龄为:" << p2.age << endl;
cout << "p3的年龄为:" << p3.age << endl;//同p2
//显示法
Person p4 = Person(10);//有参构造 注:Person(10)为匿名对象
//匿名对象特点:当前行执行结束后,系统会立即回收掉匿名对象
Person p5 = Person(p2);//拷贝构造
//注:不能利用拷贝构造函数初始化匿名对象 编译器会认为 Person (p3) 为 Person p3;对象声明 导致p3重定义
//隐式转换法
Person p6 = 10;//相当于写的是 Person p6 = Person(10);
Person p7 = p4;//拷贝构造
}
int main() {
test01();
system("pause");
return 0;
}
拷贝构造函数调用时机
C++种拷贝构造函数调用时机通常由三种情况:
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person 的无参构造函数调用" << endl;
}
Person(int age)
{
m_age = age;
cout << "Person 的有参构造函数调用" << endl;
}
//拷贝构造函数
Person(const Person &p)
{
m_age = p.m_age;
cout << "Person 的拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person 的析构函数调用" << endl;
}
int m_age;
};
void test01()//使用一个已经创建完毕的对象来初始化一个新对象
{
Person p1(20);
Person p2(p1);
cout << "p2的年龄为:" << p2.m_age << endl;
}
//值传递的方式给函数参数传值
void dowork(Person p)
{
}
void test02()
{
Person p;
dowork(p);
}
//值方式返回局部变量
Person dowork2()
{
Person p1;
return p1;
}
void test03()
{
Person p = dowork2();
}
int main() {
test01();
test02();
test03();
system("pause");
return 0;
}
构造函数调用规则
默认情况下,C++编译器至少给一个类添加3个函数
- 1、默认构造函数(无参,函数体为空)(空实现)
- 2、默认析构函数(无参,函数体为空)(空实现)
- 3、默认拷贝构造函数,对属性进行值拷贝 (值拷贝)
构造函数调用规则如下:
- 1、如果用户定义由参构造函数,C++不再提供无参构造,但是会提供默认拷贝构造
- 2、如果用户定义拷贝构造函数,C++不会再提供其他构造函数
规则1
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person 的默认构造函数调用" << endl;
}
Person(int age)
{
m_age = age;
cout << "Person 的有参构造函数调用" << endl;
}
/*
Person(const Person &p)
{
m_age = p.m_age;
cout << "Person 的拷贝构造函数调用" << endl;
}
*/
~Person()
{
cout << "Person 的析构函数调用" << endl;
}
int m_age;
};
void test01()//使用一个已经创建完毕的对象来初始化一个新对象
{
Person p;
p.m_age = 18;
Person p2(p);//编译器提供默认拷贝构造
cout << "p2的年龄为:" << p2.m_age << endl;
}
void test02()
{
//Person p1;//有有参构造无默认构造时错误,有有参构造时,函数不会提供默认构造
Person p1(18);
Person p3(p1);//编译器提供默认拷贝构造
}
int main() {
test01();
test02();
system("pause");
return 0;
}
规则2:
#include <iostream>
using namespace std;
class Person
{
public:
Person(const Person &p)
{
m_age = p.m_age;
cout << "Person 的拷贝构造函数调用" << endl;
}
~Person()
{
cout << "Person 的析构函数调用" << endl;
}
int m_age;
};
void test02()
{
//Person p; 错误
//Person p1(18); 错误
}
int main() {
test02();
system("pause");
return 0;
}
深拷贝和浅拷贝
- 浅拷贝:简单的赋值拷贝操作
- 深拷贝:在堆区重新申请空间,进行拷贝操作
错误示范:
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person 的默认构造函数调用" << endl;
}
Person(int age,int height)
{
m_age = age;
m_height = new int(height);//须由程序员手动释放
cout << "Person 的有参构造函数调用" << endl;
}
~Person()
{
//析构代码,将堆区开辟数据做释放操作
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "Person 的析构函数调用" << endl;
}
int m_age;
int *m_height;
};
void test01()
{
Person p1(18,160);
cout << "p1的年龄为:" << p1.m_age << "身高为:" << *p1.m_height << endl;
Person p2(p1);
cout << "p2的年龄为:" << p2.m_age << "身高为:" << *p2.m_height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
错误原因:
两次释放同一堆区内存空间,第二次为非法操作
利用深拷贝修改
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "Person 的默认构造函数调用" << endl;
}
Person(int age,int height)
{
m_age = age;
m_height = new int(height);//须由程序员手动释放
cout << "Person 的有参构造函数调用" << endl;
}
//自己实现拷贝构造函数, 解决浅拷贝带来的问题
Person(const Person &p)
{
cout << "Person 的拷贝构造函数调用" << 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 << "Person 的析构函数调用" << endl;
}
int m_age;
int *m_height;
};
void test01()
{
Person p1(18,160);
cout << "p1的年龄为:" << p1.m_age << "身高为:" << *p1.m_height << endl;
Person p2(p1);
cout << "p2的年龄为:" << p2.m_age << "身高为:" << *p2.m_height << endl;
}
int main() {
test01();
system("pause");
return 0;
}
如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
初始化列表
- 作用:C++提供了初始化列表语法,用来初始化属性
- 语法:构造函数():属性1(值1),属性2(值2)…{}
#include <iostream>
using namespace std;
class Person
{
public:
//传统初始化操作
/*
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)
{
}
int m_A;
int m_B;
int m_C;
};
void test01()
{
//Person p(10, 20, 30);
Person p(30,20,10);
cout << "m_A = " << p.m_A << " m_B = " << p.m_B << " m_C = " << p.m_C << endl;
}
int main() {
test01();
system("pause");
return 0;
}
注意:冒号位置
类对象作为类成员
- C++类中的成员可以时另一个类的对象,我们称该成员为对象成员
#include <iostream>
using namespace std;
#include <string>
class Phone
{
public:
Phone(string pname)
{
m_Pname = pname;
cout << "Phone的构造函数调用" << endl;
}
~Phone()
{
cout << "Phone的析构函数调用" << endl;
}
//手机品牌名称
string m_Pname;
};
class Person
{
public:
Person(string name, string pname) :m_name(name), m_phone(pname)
//相当于Phong m_phone = pname 隐式转换法
{
cout << "Person的构造函数调用" << endl;
}
~Person()
{
cout << "Person的析构函数调用" << endl;
}
//姓名
string m_name;
//手机
Phone m_phone;
};
void test01()
{
Person p("张三", "苹果MAX");
cout << p.m_name << "拿着:" << p.m_phone.m_Pname << endl;
}
int main() {
test01();
system("pause");
return 0;
}
当其他类对象作为本类成员,构造时先构造类对象,再构造自身
当其他类对象作为本类成员,析构时先析构自身,再析构对象
静态成员
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
静态成员分类:
- 静态成员变量:
– 所有对象共享同一份数据
– 在编译阶段分配内存
– 类内声明,类外初始化 - 静态成员函数:
– 所有对象共享同一个函数
– 静态成员函数只能访问静态成员变量
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
//静态成员函数
static void func()
{
m_a = 100;//静态成员函数可以访问静态成员变量 共享的,所有成员公用同一参数
// m_b = 200; 错误,静态成员函数不可以访问非静态成员变量 因为编译器不确定修改的是哪个对象的m_b属性
cout << "static void func的调用" << endl;
}
static int m_a;//静态成员变量
int m_b;//非静态成员变量
private:
static void func2()
{
cout << "static void func2的调用" << endl;
}
};
int Person::m_a = 0;
//两种访问方式
void test01()
{
//通过对象访问
Person p;
p.func();
//通过类名访问
Person::func();
//Person::func2(); 错误,类外不可以访问私有静态成员函数
}
int main() {
test01();
system("pause");
return 0;
}
C++对象模型和this指针
成员变量和成员函数分开储存
- 在C++中,类内的成员变量和成员函数分开储存
- 只有非静态成员变量才属于类的对象上
空对象占用内存空间为1
#include <iostream>
using namespace std;
#include <string>
class Person
{
};
//两种访问方式
void test01()
{
Person p;
//空对象占用内存空间
//C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
//每个空对象也应该有一个独一无二的内存地址
cout << "size of p=" << sizeof(p) << endl;
}
int main() {
test01();
system("pause");
return 0;
}
成员变量和成员函数分开储存
#include <iostream>
using namespace std;
#include <string>
class Person
{
int m_a; //非静态成员变量
static int m_b; //静态成员变量 不属于类对象上
void func()//非静态成员函数 不属于类对象上
{ }
static void func2() {}//静态成员函数 不属于类对象上
};
int Person::m_b = 0;
void test01()
{
Person p;
cout << "size of p = " << sizeof(p) << endl;
}
int main() {
test01();
system("pause");
return 0;
}
this指针概念
- this指针指向被调用的成员函数所属的对象
- this指针是隐含每一个非静态成员函数内的一种指针
- this指针不需要定义,直接使用即可
- this指针的用途:
– 当形参和成员变量同名时,可用this指针来区分
– 在类的非静态成员函数中返回对象本身,可使用return * this
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
Person(int age)
{
this->age = age;//解决名称冲突 this指针指向被调用的成员函数所属的对象
}
Person & PersonAddage(Person &p)
{
this->age += p.age;
return *this;
}
int age;
};
void test01()
{
Person p1(18);
cout << "p1的年龄为:" << p1.age << endl;
}
//返回对象本身用*this
void test02()
{
Person p1(10);
Person p2(10);
//链式编程思想
p2.PersonAddage(p1).PersonAddage(p1).PersonAddage(p1);
cout << "p2的年龄为:" << p2.age << endl;
}
int main() {
test01();
test02();
system("pause");
return 0;
}
空指针访问成员函数
- C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
- 如果用到this指针,需要加以判断保证代码的健壮性
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
void showclassname()
{
cout << "this is Person class" << endl;
}
void shoePersonage()
{
if (this == NULL)//提高代码健壮性
{
return;
}
cout << "age = " << this->m_age << endl;
}
int m_age;
};
void test01()
{
Person *p = NULL;
p->showclassname();
//p->shoePersonage(); 错误,因为传入的指针为空
}
int main() {
test01();
system("pause");
return 0;
}
const修饰成员函数
常函数
- 成员函数后加const后我们称这个函数为常数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象
- 声明对象前加const称该对象为常对象
- 常对象只能调用常函数
#include <iostream>
using namespace std;
#include <string>
class Person
{
public:
void showPeson() const//常函数
{
this->m_b = 100;
// this->m_a = 100; this指针本质是指针常量,指针的指向是不可以修改的
//成员函数后的const实际上修饰的是this指针,让指针指向的值也不可以修改
//this = NULL; this指针不可以修改指针的指向
}
void func()
{
m_a = 100;
}
int m_a;
mutable int m_b;//特殊变量,即使在常函数中,也可以修改这个值,加关键字mutable
};
void test01()
{
Person p;
p.showPeson();
}
//常对象
void test02()
{
const Person p;//在对象前加const ,变为常对象
// p.m_a = 100;
p.m_b = 100;//特殊变量,即使在常d对象下,也可以修改这个值
//常对象只能调用常函数
p.showPeson();
//p.func(); 错误,常对象不可以调用普通成员函数,因为普通成员函数可以修改属性
}
int main() {
test01();
system("pause");
return 0;
}