C++基础编程
C++入门
模板
# include <iostream>
using namespace std;
int main()
{
cout<<"hello world"<<endl
system ("pause")
return 0
}
注释
单行注释 // 描述信息
多行注释 /* 描述信息
变量
语法:数据类型 变量名 = 变量初始值
常量
不可更改的数据,定义方式:
#define 宏常量
,一般写在main外面
#define Day 7
int main(){
cout<<"一周总共有:"<<Day<<endl
system("pause")
return 0
}
const修饰的变量
一般写在main里面
int main(){
const int month = 12 // const修饰的变量也是常量
cout<<"一年总共有:"<< month <<endl
system("pause")
return 0
}
关键字
比如int float long等等
标识符命名规则
- 标识符不能是关键字
- 标识符只能由字母、数字、下划线组成
- 第一个字符必须为字母或下划线
标识符中字母区分大小写
C++常见数据类型
数据类型存在的意义:给变量分配合适的内存空间
- 整型
short(2字节)-215~215-1
int(4字节)-231~231-1
long(4字节)
long long(8字节)-263~263-1
sizeof关键字
语法:sizeof(数据类型/变量)
- 实型(浮点型)
float(4字节)7位有效数字
//默认输出一个小数,会显示出6位有效数字
int main(){
float f1 = 3.14f // f表示float
cout<<f1 <<endl
system("pause")
return 0
}
double(8字节)15-16位有效数字
科学计数法
int main(){
float f1 = 3e2 // 3 * 10^2
float f1 = 3e-2 // 3 * 0.1^2
cout<<f1 <<endl
system("pause")
return 0
}
- 字符型
语法:char ch = ‘a’
C中字符型变量只占用1字节,
字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放到存储单元(a-97;A-65)
# include <iostream>
using namespace std;
int main()
{
/*1.字符型变量创建方式
* 2.字符型变量所占内存大小
* 3.字符型变量常见错误
* char ch2 = "b" 要用单引号
* char ch2 = ‘abc’单引号内只能由一个字符
* 4.字符型变量对应ASCII码
*/
char ch = 'c';
cout << ch << endl;
cout << sizeof(ch) << endl;
cout << int(ch) << endl;
system("pause");
return 0;
}
- 转义字符
作用:用来表示一些不能显示出来的ASCII字符
现阶段常用的转义字符有:\n \ \t
# include <iostream>
using namespace std;
#define Day 7
int main()
{
//转义字符
//换行符\n
cout << "hello world\n" << endl;
//反斜杠(\\)
cout << "\\" << endl;
//水平制表符\t 整齐地输出数据
cout << "aaaa\t hello world" << endl;
cout << "aaa\t hello world" << endl;
cout << "aaaaaaa\t hello world" << endl;
cout << "aaaaa\t hello world" << endl;
system("pause");
return 0;
}
- 字符串型
1.C风格字符串:char 变量名[ ] = "字符串值"
2.C++风格字符串:string 变量名 = "字符串值"
# include <iostream>
using namespace std;
# include <string>
int main()
{
char str[] = "hello world";
// char字符串名[]
// 等号后面用双引号
string str2 = "hello world";
// 包含一个头文件
cout << str << endl;
cout << str2 << endl;
system("pause");
return 0;
}
- 布尔类型bool
# include <iostream>
using namespace std;
int main()
{
bool flag = true;
cout << flag << endl;
system("pause");
return 0;
}
- 数据的输入
关键字cin
语法:cin>>输入
# include <iostream>
using namespace std;
# include <string>
int main()
{
//1.int
int s = 0;
cout << "请给int量赋值:" << endl;
cin >> s;
cout << "int变量s=" << s << endl;
//2.float
float f = 3.14f;
cout << "请给float变量赋值:" << endl;
cin >> f;
cout << "float变量f=" << f << endl;
//3.char
char ch = 'a';
cout << "请给字符型变量ch赋值" << endl;
cin >> ch;
cout << "字符型变量ch=" << ch << endl;
// str
string str = "hello";
cout << "请给字符串型变量赋值" << endl;
cin >> str;
cout << "字符串变量str=" << str << endl;
// bool
bool flag = false;
cout << "布尔类型flag赋值" << endl;
cin >> flag;
cout << "flag=" << flag << endl;
system("pause");
return 0;
}
C++运算符
算数运算符
# include <iostream>
using namespace std;
# include <string>
int main()
{
//加减乘除
int n1 = 10;
int n2 = 3;
cout << n1 + n2 << endl;
cout << n1 - n2 << endl;
cout << n1 * n2 << endl;
cout << n1 / n2 << endl;
//取模运算(求余数)
cout << n1 % n2 << endl;
// 前置递增,先让变量+1再进行表达式运算
int n3 = ++n1 * 10;
cout << "n1=" << n1 << endl;
cout << "n3=" << n3 << endl;
// 后置递增,先进行表达式运算,后让变量+1
int n4 = 10;
int n5 = n4++ * 10;
cout << "n4=" << n4 << endl;
cout << "n5=" <<n5<< endl;
// 前置递减
int n6 = 10;
int n7 = --n6;
cout << "n6=" << n6 << endl;
cout << "n7=" << n7 << endl;
// 后置递减
int n8 = 10;
int n9 = n8--;
cout << "n8=" << n8 << endl;
cout << "n9=" << n9 << endl;
system("pause");
return 0;
}
赋值运算符
+=; -=;*=;/=;%=
比较运算符
==;!=; <; >; <=; >=
逻辑运算符
!(非); &&(与); ||(或)
# include <iostream>
using namespace std;
int main()
{
//逻辑运算符非!
int b = 10;
cout << !b << endl; //0
cout << !!b << endl; //1
// &&
int a = 10;
cout << (a && b) << endl; //1
// ||
int c = 0;
cout << (a||b) << endl; //1
cout << (a||c) << endl; //1
system("pause");
return 0;
}
C++程序流程结构
顺序结构、选择结构、循环结构
if语句
*单行格式的if语句*
# include <iostream>
using namespace std;
int main()
{
//选择结构 单行if语句
//用户输入一个分数,判断考上大学否
int score = 0;
//用户输入一个分数
cout << "请输入一个分数:" << endl;
cin >> score;
//打印这个分数
cout << "您输入的分数是:" << score << endl;
//判断这个分数是否超过一本线
if(score>500)
{
cout << "恭喜您考上了一本大学" << endl;
}
//if条件后面不要加分号
system("pause");
return 0;
}
*多条件的if语句*
# include <iostream>
using namespace std;
int main()
{
//选择结构多条件if语句
//用户输入一个分数,判断考上的大学
int score = 0;
//用户输入一个分数
cout << "请输入一个分数:" << endl;
cin >> score;
//打印这个分数
cout << "您输入的分数是:" << score << endl;
//判断这个分数是否超过一本线,打印考上一本,否则未考上大学
if(score>600)
{
cout << "恭喜您考上了一本大学!" << endl;
}
else if(score>500)
{
cout << "恭喜您考上了二本大学!" << endl;
}
else if(score>400)
{
cout << "恭喜您考上了三本大学!" << endl;
}
else
{
cout << "分数不满足,请再接再厉!" << endl;
}
//if条件后面不要加分号
system("pause");
return 0;
}
*嵌套格式的if语句*
# include <iostream>
using namespace std;
int main()
{
//选择结构多条件if语句
//用户输入一个分数,判断考上的大学
int score = 0;
//用户输入一个分数
cout << "请输入一个分数:" << endl;
cin >> score;
//打印这个分数
cout << "您输入的分数是:" << score << endl;
//判断这个分数是否超过一本线,打印考上一本,否则未考上大学
if(score>600)
{
cout << "恭喜您考上了一本大学!" << endl;
if(score>700)
{
cout << "您能考上北京大学!" << endl;
}
else if(score>650)
{
cout << "您能考上清华大学!" << endl;
}
else
{
cout << "您能考上人民大学!" << endl;
}
}
else if(score>500)
{
cout << "恭喜您考上了二本大学!" << endl;
}
else if(score>400)
{
cout << "恭喜您考上了三本大学!" << endl;
}
else
{
cout << "分数不满足,请再接再厉!" << endl;
}
//if条件后面不要加分号
system("pause");
return 0;
}
*多行格式的if语句*
# include <iostream>
using namespace std;
int main()
{
//选择结构 单行if语句
//用户输入一个分数,判断考上大学否
int score = 0;
//用户输入一个分数
cout << "请输入一个分数:" << endl;
cin >> score;
//打印这个分数
cout << "您输入的分数是:" << score << endl;
//判断这个分数是否超过一本线,打印考上一本,否则未考上大学
if(score>500)
{
cout << "恭喜您考上了一本大学!" << endl;
}
else
{
cout << "您未考上一本大学,请再接再厉!" << endl;
}
//if条件后面不要加分号
system("pause");
return 0;
}
小猪称体重案例
# include <iostream>
using namespace std;
int main()
{
//三只小猪称体重,判断哪知最重
//创建三只小猪的体重变量
int num1 = 0;
int num2 = 0;
int num3 = 0;
//用户输入三只小猪的重量
cout << "请输入小猪A的体重:" << endl;
cin >> num1;
cout << "请输入小猪B的体重:" << endl;
cin >> num2;
cout << "请输入小猪C的体重:" << endl;
cin >> num3;
cout << "小猪A的体重为:" << num1 <<endl;
cout << "小猪B的体重为:" << num2 << endl;
cout << "小猪C的体重为:" << num3 << endl;
//判断谁最重
//先比较A和B
if (num1 > num2)
{
if (num1 > num3)
{
cout << "小猪A最重。" << endl;
}
else
{
cout << "小猪C最重。" << endl;
}
}
else
{
if (num2 > num3)
{
cout << "小猪B最重。" << endl;
}
if (num2 < num3)
{
cout << "小猪C最重。" << endl;
}
}
system("pause");
return 0;
}
三目运算符
语法:表达式1?表达式2:表达式3
如果表达式1为真,执行表达式2,并返回表达式2的结果;
如果表达式1为假,执行表达式3,并返回表达式3的结果。
# include <iostream>
using namespace std;
int main()
{
//三目运算符?:
//创建三个变量abc将ab比较,将较大的赋值给c
int a = 10;
int b = 20;
int c = 0;
c = (a > b) ? a : b;
cout << c << endl;
//在C++中三目运算符返回的是变量,可以继续赋值
(a < b ? a : b) = 100;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
system("pause");
return 0;
}
switch语句
执行多条件分支语句
判断时候只能是整型或者字符型,不可以是一个区间
如果不写break程序会一直向下走
优点是运行效率高,结构清楚。
# include <iostream>
using namespace std;
int main()
{
//switch语句
//给电影打分
//提示用户进行打分
int score = 0;
cout << "请您给电影进行打分:" << endl;
cin >> score;
cout << "您打的分数是:" << score << endl;
switch (score)
{
case 10:
cout << "您认为这是经典电影。" << endl;
break; //推出当前分支
case 9:
cout << "您认为这是经典电影。" << endl;
break;
case 8:
cout << "您认为电影非常好。" << endl;
break;
case 7:
cout << "您认为电影很好。" << endl;
break;
case 6:
cout << "您认为电影及格。" << endl;
break;
default:
cout << "您认为这是一个烂片。" << endl;
}
system("pause");
return 0;
}
循环结构
while循环语句
while(循环条件)(循环语句)
# include <iostream> using namespace std; int main() { //while语句 //在屏幕中打印0-9这10个数字 //避免死循环 int num = 0; while (num < 10) { cout << num << endl; num++; } system("pause"); return 0; }
猜数字案例
# include <iostream>
using namespace std;
# include <ctime>
int main()
{
//添加随机数种子 利用当前系统时间生成随机数,防止每次随机数都一样
srand((unsigned int)time(NULL));
//系统生成一个随机数
int num = rand() % 100 + 1;
//cout << num << endl;
while (1)
{
int val = 0;
cout << "请输入猜测值:" << endl;
cin >> val;
//判断玩家的猜测
if (val > num)
{
cout << "猜测过大。" << endl;
}
else if (val < num)
{
cout << "猜测过小." << endl;
}
else
{
cout << "恭喜您猜对了!" << endl;
break;
}
}
system("pause");
return 0;
}
do……while循环语句
do(循环语句)while(循环条件)
# include <iostream>
using namespace std;
int main()
{
int num = 0;
do
{
cout << num << endl;
num++;
}
while (num<10);
system("pause");
return 0;
}
水仙花数案例
# include <iostream>
using namespace std;
int main()
{
//先打印所有的三位数
int num = 100;
do
{
//判断水仙花数
int a = 0; //个位数
int b = 0; //十位数
int c = 0; //百位数
a = num % 10;
b = num / 10 % 10;
c = num / 100;
if (a * a * a + b * b * b + c * c * c == num) //是水仙花才打印
{
cout << num << endl;
}
num++;
}
while (num < 1000);
system("pause");
return 0;
}
for 循环
语法:for(起始表达式;条件表达式;末尾循环体){循环语句}
//起始表达式只执行一次
# include <iostream>
using namespace std;
int main()
{
//for 循环语句
//打印从0到9
for (int i = 0; i < 10; i++)
{
cout << i << endl;
}
system("pause");
return 0;
}
敲桌子案例
# include <iostream>
using namespace std;
int main()
{
//敲桌子案例
//0-100个数,遇到个位数是7(%10=7)、7的倍数(%7=0)、十位数是7(/10=7)不输出,改为输出“敲桌子”
for (int i = 0; i <= 100; i++)
{
if (i % 10 == 7 || i % 7 == 0 || i / 10 == 7)
{
cout << "敲桌子" << endl;
}
else
{
cout << i << endl;
}
}
system("pause");
return 0;
}
嵌套循环
# include <iostream>
using namespace std;
int main()
{
//利用嵌套循环打印星图
//外层循环
//外层执行一次,内层循环一周
for (int i = 0; i < 10; i++)
{
//内层循环
for (int j = 0; j < 10; j++)
{
cout << "*" ;
}
cout << endl;
}
system("pause");
return 0;
}
案例乘法口诀表
列数 * 行数 = 计算结果
列数 <= 行数
#include<iostream>
using namespace std;
int main()
{
//打印乘法口诀
for (int i = 1; i <= 9; i++) {
// cout << i << endl;
for (int j = 1; j <= i; j++) {
cout << j<<"*"<< i << "=" << j * i<<" ";
}
cout << endl;
}
system("pause");
return 0;
}
跳转语句
break语句 作用:用于跳出选择结构或循环结构
-
出现在switch条件语句中,作用是终止case并跳出switch
-
出现在循环语句中,作用是跳出当前的循环语句
-
出现在嵌套循环中,跳出最近的内层循环语句
#include<iostream> using namespace std; int main() { //break //1.出现在switch中 cout << "请选择副本难度:" << endl; cout << "1:简单;2:中等;3:困难" << endl; int select = 0; cin >> select; switch (select) { //必须是case 1,这个1前面必须有空格 case 1: cout << "您选择了普通难度" << endl; break; case 2: cout << "您选择了中等难度" << endl; break; case 3: cout << "您选择了困难难度" << endl; break; default: break; } //2.出现在循环中 for (int i = 0; i < 10; i++) { if (i == 5) { break; } cout << i << endl; } //3.出现在嵌套循环中 for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (j == 5) { break; } cout << "* "; } cout << endl; } system("pause"); return 0; }
跳转语句-continue语句
*作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环*
代表这次循环在continue处戛然而止
#include<iostream>
using namespace std;
int main()
{
//continue
for (int i = 0; i <= 100;i++)
{
//如果是奇数输出,偶数不输出
if (i % 2 == 0)
{
continue; //可以筛选条件,执行到此不再向下执行,执行下一次循环
}
cout << i << endl;
}
system("pause");
return 0;
}
跳转语句-goto语句
*作用:无条件跳转语句*
C++数组
一维数组基础知识
数组 *就是一个集合,里面存放了相同类型的数据元素*
特点:相同类型;内存连续
数据类型 数组名 [数组长度];
数据类型 数组名[数组长度] = {值1,值2,……};
数据类型 数组名[] = {值1, 值2,……}
#include<iostream>
using namespace std;
int main()
{
//数组
/*1. `数据类型 数组名 [数组长度];`
`数据类型 数组名[数组长度] = {值1,值2,……};`
`数据类型 数组名[] = {值1, 值2,……}`
*/
//1
int arr[5];
//利用下表对数组元素进行赋值
arr[0] = 1;
arr[1] = 2;
arr[2] = 4;
arr[3] = 5;
arr[4] = 6;
cout << arr[0] << endl;
system("pause");
return 0;
}
//2.
// 如果不初始化,系统会用0填充
int arr2[5] = { 10, 20, 30, 40 };
for (int i = 0; i < 5; i++)
{
cout << arr2[i] << endl;
}
//3
//必须初始化所有数组元素
int arr3[] = { 10, 20, 30 };
一维数组数组名
名称的用途:可以统计整个数组在内存中的长度;可以获取数组在内存中的首地址;
数组名是常量,不可以进行赋值操作
#include<iostream>
using namespace std;
int main()
{
//一维数组名称
int arr[5];
cout << sizeof(arr) << endl; //20(5*4)
cout << sizeof(arr[0]) << endl; //4
cout << sizeof(arr) / sizeof(arr[0]) << endl; //5
cout << "数组的首地址是:" << (int)arr << endl; //(int)将地址转为10进制查看
cout << "数组中第一个元素地址为:" << (int) & arr[0] << endl; //&取地址符号
system("pause");
return 0;
}
数组案例-五只小猪称体重
#include<iostream>
using namespace std;
int main()
{
//创建五只小猪体重的数组
int arr[5] = { 100, 400, 300, 520, 700 };
//从数组中找到最大值
int max = 0; //先认定最大值0
for (int i = 0; i < 5; i++)
{
if (arr[i] > max) {
max = arr[i];
} //便历经所有小猪体重,如果比max大,就更新max
}
//打印最大值
cout <<"max="<< max << endl;
system("pause");
return 0;
}
数组案例-数组元素逆置
#include<iostream>
using namespace std;
int main()
{
//实现数组元组中的元素逆置
int arr[] = { 1, 3, 2, 5, 4, 6 };
cout << "逆置前的结果是:" << endl;
for (int i =0 ; i <6; i++) {
cout << arr[i];
}
cout << endl;
int start = 0; //起始下标
int end = sizeof(arr) / sizeof(arr[0]) - 1; //终止下标
while (start < end)
{
int tmp = arr[start];
arr[start] = arr[end];
arr[end] = tmp;
start++;
end--;
}
cout << "逆置后的结果是:" << endl;
for (int j = 0; j < 6; j++) {
cout << arr[j];
}
cout << endl;
system("pause");
return 0;
}
冒泡排序
最常用的排序算法,对数组内元素进行排序
#include<iostream>
using namespace std;
int main()
{
//利用冒泡排序实现升序序列
int arr[] = { 4, 2, 5, 6, 7, 8, 1, 10 };
cout << "排序之前:" << endl;
for (int i = 0; i < 8; i++)
{
cout << arr[i]<<" ";
}
cout << endl;
//排序轮数=元素个数-1
//每轮对比次数=元素个数-排序轮数-1
for (int i = 0; i < 8 - 1; i++)
{
//内存循环比较相邻数据
for (int j = 0; j < 8 - i - 1; j++)
{
//如果前面的元素大于相邻的后一个元素,则交换这两个元素
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
//排序后结果
cout << "排序之后:" << endl;
for (int i = 0; i < 8; i++)
{
cout << arr[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
二维数组基础知识
二维数组就是在一维数组上,多加一个维度。
定义方式:
-
数据类型 数组名[ 行数 ][ 列数 ];
-
**数据类型 数组名[ 行数 ][ 列数 ] = { {数据1, 数据2},{数据3, 数据4}};**
-
数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4 } ;
-
数据类型 数组名[][ 列数 ] = { 数据1,数据2,数据3,数据4 } ;
#include<iostream> using namespace std; int main() { // 二维数组 /* 数据类型 数组名[ 行数 ][ 列数 ]; 数据类型 数组名[ 行数 ][ 列数 ] = { {数据1, 数据2},{数据3, 数据4}}; 数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4 } ; 数据类型 数组名[][ 列数 ] = { 数据1,数据2,数据3,数据4 } ; */ //1. int arr[2][3]; // 赋值 arr[0][0] = 1; arr[0][1] = 2; arr[0][2] = 3; arr[1][0] = 4; arr[1][1] = 5; arr[1][2] = 6; //print for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { cout << arr[i][j] << endl; } } cout << arr << endl; //2 int arr2[2][3] = { {1, 2, 3}, {4, 5, 6} }; // print for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { cout << arr2[i][j] << " "; } cout << endl; } // 3 int arr3[2][3] = { 1, 2, 3, 4, 5, 6 }; // print for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { cout << arr3[i][j] << " "; } cout << endl; } //4 int arr4[][3] = { 1, 2, 3, 4, 5, 6 }; // print for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { cout << arr4[i][j] << " "; } cout << endl; system("pause"); return 0; }
二维数组名称
作用:查看二维数组所占内存空间;获取二维数组首地址
#include<iostream>
using namespace std;
int main()
{
// 查看占用空间大小
int arr[2][3] = {
{1,2,3},
{4, 5, 6}
};
cout << "二维数组占用内存为" << sizeof(arr) << endl; //24(4*6)
cout << "二维数组一行占用内存为" << sizeof(arr[0]) << endl; //12
cout << "二维数组第一个元素占用内存为" << sizeof(arr[0][0]) << endl; //4
cout << "arr的行数" << sizeof(arr) / sizeof(arr[0]) << endl;
cout << "arr的列数" << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;
// 查看二维数组的地址
cout << "arr首地址为" << (int)arr << endl;
cout << "arr第一个元素的地址" << (int)&arr[0][0] << endl; //&取址符号
system("pause");
return 0;
}
数组案例—考试成绩统计
#include<iostream>
using namespace std;
#include <string>
int main()
{
// 1.创建二维数组,保存成绩(3,3)
int score[3][3] =
{
{100, 100, 100},
{60, 50, 100},
{60, 70, 50}
};
string name[3] = {"张三","李四","王五"};
// 2.统计考试成绩,让每行三列相加
for (int i = 0; i < 3; i++)
{
int sum = 0;
for (int j = 0; j < 3; j++)
{
sum += score[i][j];
}
cout <<name[i] << "的总成绩为:" << sum << endl;
}
system("pause");
return 0;
}
C++函数
函数定义
作用:将一段经常使用的代码封装起来,减少重复代码
定义的5步骤:
-
返回值类型
-
函数名
-
参数列表
-
函数体语句
-
return表达式
#include<iostream> using namespace std; #include <string> int main() { system("pause"); return 0; } // 实现一个加法函数 int add(int num1, int num2) { int sum = num1 + num2; return sum; }
函数调用
#include<iostream>
using namespace std;
#include <string>
// 实现一个加法函数
// num1和num2只是形式上的参数,简称形参
int add(int num1, int num2)
{
int sum = num1 + num2;
return sum;
}
int main()
{
//main 中调用add函数
int a = 10;
int b = 20;
// 函数调用语法:函数名称(参数)
// a和b称为实际参数,简称实参
// 调用的时候,实参的值传给形参
int c = add(a, b);
cout << c << endl;
system("pause");
return 0;
}
值传递
#include<iostream>
using namespace std;
// 值传递
// 定义函数,实现两个数字交换
// 如果一个函数不需要返回值,声明的时候可以写void
void swap(int num1, int num2)
{
cout << "交换前:" <<" num1= "<< num1<<" num2= "<<num2<< endl;
int tmp = num1;
num1 = num2;
num2 = tmp;
cout << "交换后:" << " num1= " << num1 << " num2= " << num2 << endl;
return;
}
int main()
{
int a = 10;
int b = 20;
cout << "a = " << a << "b = " << b << endl;
swap(a, b);
// 当我们做值传递的时候,函数的形参发生改变,并不会影响实参
cout << "a = " << a << "b = " << b << endl;
system("pause");
return 0;
}
==**值传递时,形参修饰不了实参。**
==
函数的常见样式
-
无参无返
-
有参无返
-
无参有返
-
有参有返
#include<iostream> using namespace std; // 1.无参无返 void test01() { cout << "this is test01" << endl; } // 2. void test02(int a) { cout << "this is test02:" << a << endl; } // 3 int test03() { cout << "this is test03" << endl; return 1000; } // 4 int test04(int a, int b) { cout << "this is test04" << endl; return a + b; } int main() { test01(); test02(100); int num1 = test03(); cout << num1<<endl; int num2 = test04(100, 2000); cout << num2 << endl; system("pause"); return 0; }
函数声明
如果函数定义在main函数后面,前面必须写函数声明。
声明可以有多次,但是定义只能有一次。
#include<iostream>
using namespace std;
// 提前告诉编译器函数的存在,可以利用函数的声明
// 函数的声明
int max(int a, int b);
int main()
{
cout << max(1000, 20) << endl;
system("pause");
return 0;
}
// 定义
int max(int a, int b)
{
return a > b ? a : b;
}
函数的分文件编写
作用:让代码结构更加清晰
步骤:
-
*创建后缀名为.h的头文件*
-
*创建后缀名为.cpp的源文件*
-
*在头文件中写函数的声明*
-
*在源文件中写函数的定义*
注意头文件在调用时候,必须include才可使用
#pragma once // 头文件 // 声明 void swap(int a, int b);
# include <iostream>
using namespace std;
# include "swap.h"
// 源文件
// 定义
void swap(int a, int b)
{
int tmp = b;
b = a;
a = tmp;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
}
# include <iostream>
using namespace std;
# include"swap.h"
// 包含主函数的源文件
int main()
{
int a = 10;
int b = 20;
swap(a, b);
system("pause");
return 0;
}
C++指针(p)
指针基础知识
作用:可以通过指针间接访问内存(指针可以保存一个地址)
内存编号是从0开始记录的,一般用十六进制数字表示
可以利用指针变量保存地址
# include <iostream>
using namespace std;
int main()
{
// 定义指针
int a = 10;
// 语法定义
int* p;
//让指针记录变量a的地址
p = &a;
cout << "a的地址是:" << &a << endl;
cout << "指针p是:" << p << endl;
// 使用指针
// 可以通过解引用的方式来找到指针指向的内存
// *p
*p = 1000;
cout << "a= " << a << endl;
cout << "*p= " << *p << endl;
system("pause");
return 0;
}
指针所占的内存空间
# include <iostream>
using namespace std;
int main()
{
// 指针所占的内存空间
// 在64位操作系统中,指针占用8个字节,与数据类型无关
// 在32位操作系统中,指针占用4个字节
int a = 10;
int* p = &a;
cout << "sizeof(int*): " << sizeof(int*) << endl; //8
cout << "sizeof(p) " << sizeof(p) << endl;
cout << "sizeof(double*) " << sizeof(double*) << endl; //8
system("pause");
return 0;
}
空指针
指针变量指向内存中编号为0的空间的指针
作用:初始化指针变量
(不知道指针指哪可以先指这)
空指针指向的内存是不可以更改的
野指针
指针变量指向非法的内存空间
# include <iostream>
using namespace std;
int main()
{
// 野指针
// 在程序中避免出现
int* p = (int*)0x1100;
system("pause");
return 0;
}
const修饰指针
-
const修饰指针——常量指针
(const *) -
const修饰常量——指针常量
-
const修饰指针,又修饰常量
我的理解:const修饰之后,指针不是不能改,而是不能通过某种方式改,比如指针常量,指针指向的值可以改,但是不能通过改变其地址的方式修改。
# include <iostream>
using namespace std;
int main()
{
// const *
int a = 10;
int b = 20;
const int* p = &a;
cout << *p << endl; // 10
// 指针指向的值不可以更改,指针指向可以更改
// *p = 20;错误
p = &b; // true
cout << *p << endl; // 20
// * const
int* const p2 = &a;
cout << *p2 << endl; // 10
// 指针的指向不可以改,指针指向的值可以改
// p2 = &b; false
*p2 = 20;
cout << *p2 << endl; // 20
cout << "a= " << a << endl; // a=20
// const修饰指针和变量
const int* p3 = &a;
// *p3 = b; False
// *p3 = 20; False
system("pause");
return 0;
}
指针和数组
利用指针访问数组元素
# include <iostream>
using namespace std;
int main()
{
// 指针和数组
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* p = arr; //arr是数组的首地址
cout << "arr的第一个元素是:" << *p << endl;
p++; // 往后偏移4个字节
cout << "arr的第二个元素是:" << *p << endl;
cout << "利用指针遍历数组" << endl;
int* p2 = arr;
for (int i = 0; i < 10;i++)
{
cout << *p2 << endl;
p2++;
}
system("pause");
return 0;
}
指针和函数
利用指针作为函数参数,可以修改实参的值
# include <iostream>
using namespace std;
void swap(int a, int b)
{
int tmp = b;
b = a;
a = tmp;
cout << "swap a=" << a << endl;
cout << "swap b=" << b << endl;
}
void swap02(int* p1, int* p2)
{
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
int main()
{
// 指针和函数
// 1.值传递
int a = 10;
int b = 20;
swap(a, b);
cout << "a=" << a << endl; //10
cout << "b=" << b << endl; //20
// 值传递不能修饰实参
// 地址传递
swap02(&a, &b);
cout << "a=" << a << endl; //20
cout << "b=" << b << endl; //10
// 地址传递可以修饰实参
system("pause");
return 0;
}
指针、数组、函数、案例
—封装一个函数,利用冒泡排序,实现对数组的升序排序
*我的理解:函数的参数是数组的时候,要把数组当作整体,用地址访问数组,只需知道首地址和数组的长度。*
# include <iostream>
using namespace std;
//创建函数实现冒泡排序 参数1 数组的首地址 参数2 数组的长度
void bubbleSort(int* arr,int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (arr[j] > arr[j + 1] )
{
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
// 打印数组
void printArray(int *arr,int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << endl;
}
}
int main()
{
//1.创建数组
int arr[10] = { 4, 3, 6, 9, 1, 2, 10, 8, 7, 5 };
int len = sizeof(arr) / sizeof(arr[0]);
//创建函数实现冒泡排序
bubbleSort(arr, len);
//展示结果
printArray(arr, len);
return 0;
}
C++结构体
结构体基础知识
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。
语法:struct 结构体名 {结构体成员列表}
;
# include<iostream>
using namespace std;
# include<string>
// 1、创建学生数据类型:包括(姓名、年龄、分数)
// 自定义的数据类型,一些类型集合组成的一个类型
struct Student
{
// 成员列表
// 姓名
string name;
// 年龄
int age;
// 分数
int score;
}s3; //顺便创建结构体变量
// 2、通过学生类型创建具体学生
int main()
{
// 2.1 struct Student s1
// 创建变量的时候struct关键字可以省略
Student s1;
s1.name = "张三";
s1.age = 18;
s1.score = 500;
cout << "姓名: " << s1.name << "年龄: " << s1.age << "成绩: " << s1.score << endl;
// 2.2 struct Student s2 = {...}
struct Student s2 = {"张三", 19, 100};
cout << " 姓名: " << s2.name << " 年龄: " << s2.age << " 成绩: " << s2.score << endl;
// 2.3 在定义结构体时顺便创建结构体变量
s3.name = "张三";
s3.age = 18;
s3.score = 500;
cout << " 姓名: " << s3.name << " 年龄: " << s3.age << " 成绩: " << s3.score << endl;
system("pause");
return 0;
}
结构体数组
将自定义的结构体放入数组中方便维护
语法:struct 结构体名 数组名[元素个数] = { {},{},{} }
# include<iostream>
using namespace std;
# include<string>
// 1、定义结构体
struct Student
{
// 成员列表
// 姓名
string name;
// 年龄
int age;
// 分数
int score;
};
int main()
{
// 2、创建结构体数组
struct Student stuArray[3] =
{
{"张三", 18, 200},
{"李四", 19, 150},
{"王五", 16, 300}
};
// 3、给结构体数组中的元素赋值
stuArray[2].name = "赵六";
stuArray[2].age = 30;
// 4、遍历这个数组
for (int i = 0; i < 3; i++)
{
cout << "name: " << stuArray[i].name << " age: " << stuArray[i].age << " score: " << stuArray[i].score << endl;
}
system("pause");
return 0;
}
结构体指针
通过指针访问结构体中的成员
->可以通过结构体指针访问结构体属性
# include<iostream>
using namespace std;
# include<string>
// 1、定义结构体
struct Student
{
// 成员列表
// 姓名
string name;
// 年龄
int age;
// 分数
int score;
};
int main()
{
// 2、创建结构体变量
struct Student s1 = { "张三", 19, 200 };
// 3、通过指针指向结构体变量
struct Student* p = &s1;
// 4、通过指针访问结构体变量中的数据
cout << "name: " << p->name << " age: " << p->age << " score: " << p->score << endl;
system("pause");
return 0;
}
结构体嵌套结构体
结构体中的成员可以是另一个结构体
# include<iostream>
using namespace std;
# include<string>
struct student
{
string name;
int age;
int score;
};
struct teacher
{
string name;
int id;
int age;
student stu;
};
int main()
{
teacher t;
t.name = "Mr. Wang";
t.age = 36;
t.id = 482133;
t.stu.name = "张三";
t.stu.age = 19;
t.stu.score = 500;
cout << t.stu.score << endl;
system("pause");
return 0;
}
结构体做函数参数
值传递 and 地址传递
地址传递可以修饰实参
# include<iostream>
using namespace std;
# include<string>
struct student
{
string name;
int age;
int score;
};
void printStudent1(student s)
{
cout << "子函数中的name: " << s.name << " 子函数1中的age:" << s.age << " 子函数1中的score:" << s.score<<endl;
}
void printStudent2(student *p)
{
p->name = "李四";
cout << "子函数2中的name:" << p->name << endl;
}
int main()
{
student s;
s.name = "张三";
s.age = 19;
s.score = 200;
//printStudent1(s);
printStudent2(&s);
cout << "main中的name:" << s.name << endl;
system("pause");
return 0;
}
结构体中const使用场景
防止误操作
将函数中的形参改为指针,可以减少内存空间,而且不会复制新的副本出来。为了防止函数中对实参的写操作,需要添加const。
# include<iostream>
using namespace std;
# include<string>
struct student
{
string name;
int age;
int score;
};
void printStudent2(const student *p)
{
// p->name = "李四"; 操作失败,不可修改
cout << "子函数2中的name:" << p->name << endl;
}
int main()
{
student s;
s.name = "张三";
s.age = 19;
s.score = 200;
//printStudent1(s);
printStudent2(&s);
cout << "main中的name:" << s.name << endl;
system("pause");
return 0;
}
结构体案例
案例介绍:
设计学生和 老师结构体,在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员;
学生结构体中有姓名、考试分数、创建数组存放3名老师,通过函数给每个老师及所带的学生赋值。
# include<iostream>
using namespace std;
# include<string>
# include<ctime>
// 创建学生结构体
struct Student
{
string sname;
int score;
};
// 创建教师结构体
struct Teacher
{
string tname;
struct Student stuArray[5];
};
// 定义为结构体数组赋值的函数
void allocateSpace(struct Teacher tArray[], int len)
{
string nameSeed = "ABCDE";
for (int i = 0; i < len; i++)
{
// 姓名赋值
tArray[i].tname = "Teacher_";
tArray[i].tname += nameSeed[i];
// stu数组赋值
for (int j = 0; j < 5; j++)
{
tArray[i].stuArray[j].sname = "Stu_";
tArray[i].stuArray[j].sname += nameSeed[j];
int random = rand() % 61 + 40; //40-100之间的随机数
tArray[i].stuArray[j].score = random;
}
}
}
// 定义打印结构体数组中内容的函数
void printInfo(struct Teacher tArray[], int len)
{
for (int i = 0; i < len; i++)
{
cout << "tname= " << tArray[i].tname << endl;
for (int j = 0; j < 5; j++)
{
cout << "\tsname= " << tArray[i].stuArray[j].sname
<< " score= " << tArray[i].stuArray[j].score << endl; // \t是缩进字符
}
}
}
int main()
{
// 完全随机
srand((unsigned int)time(NULL));
// 创建teacher结构体数组
struct Teacher tArray[3];
// 调用赋值函数
int len = sizeof(tArray) / sizeof(tArray[0]);
allocateSpace(tArray, len);
// 调用打印函数
printInfo(tArray, len);
system("pause");
return 0;
}
C++核心编程
面向对象编程技术详细讲解
内存分区模型
代码区
存放函数体的二进制代码,由操作系统进行管理的(特点:存放二进制的机器指令;共享;只读)
全局区
存放全局变量和静态变量以及常量
# include<iostream>
using namespace std;
// 创建全局变量
int g_a = 20;
const int c_a = 20;
int main()
{
// 创建普通局部变量
int a = 10;
cout << "局部变量a的地址是:" << (int) & a << endl;
cout << "全局变量b的地址是:" << (int) & g_a << endl;
// 静态变量static关键字
static int s_a = 50;
cout << "静态变量s_a的地址是:" << (int) & s_a << endl;
// 字符串常量
cout << "字符串常量的地址是:" << (int)&"hello" << endl;
// const
cout << "全局常量的地址是:" << (int)&c_a << endl;
const int l_c_a = 20;
cout << "局部常量的地址是:" << (int)&l_c_a << endl;
system("pause");
return 0;
}
全局区数据:全局变量、常量(const修饰的全局变量、字符串常量)、静态变量
以上皆是程序运行前就存在的区域
栈区
由编译器自动分配释放,存放函数的参数值、局部变量等
注意<不要返回局部变量的地址,栈区开辟的数据由编译器自动释放>
# include<iostream>
using namespace std;
// 栈区数据的注意事项
// 不要返回局部变量的地址
int* func()
{
int a = 10; // 局部变量,存放在栈区
return &a; // 返回局部变量的地址
}
int main()
{
int* p = func();
cout << *p << endl; // 第一次可以打印正确的数字,是因为编译器做了保留
cout << *p << endl; // 第二次这个数据就不再保留了
cout << *p << endl;
system("pause");
return 0;
}
堆区
由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
# include<iostream>
using namespace std;
int* func()
{
int* p = new int(10);
// new返回的该数据类型的指针
return p;
}
void test01()
{
int* p = func();
cout << *p << endl;
cout << *p << endl;
cout << *p << endl;
// 利用关键字delete进行释放
delete p;
// cout << *p << endl; 内存已经被释放,再次访问就是非法操作,会报错
}
void test02()
{
int* arr = new int [10]; // 中括号代表10个元素
for (int i = 0; i < 10; i ++ )
{
arr[i] = 100 + i;
}
for (int i=0; i < 10; i++)
{
cout << arr[i] << endl;
}
// 释放数组的时候要加[]
delete[] arr;
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
不同区域存放的数据,赋予不同的生命周期,给我们更灵活的编程
C++引用
引用的基本语法
作用:给变量起别名
语法:数据类型 &别名 = 原名
# include<iostream>
using namespace std;
int main()
{
int a = 0;
int& b = a;
cout << "b= " << b << endl; // 0
b = 10;
cout << "b= " << b << endl; // 10
cout << "a= " << a << endl; // 10
system("pause");
return 0;
}
引用的注意事项
引用必须初始化;
初始化之后不可以再更改引用;
# include<iostream>
using namespace std;
int main()
{
int a = 0;
// int& b; 错误,未初始化变量b
int& b = a;
cout << "b= " << b << endl; // 0
int c = 10;
// int& b = c; 错误,不可以更改b
b = c; // 正确,这是赋值操作
cout << "a= " << a << endl; // 10
system("pause");
return 0;
}
引用做函数参数
引用传递也可以修饰实参,可以替代地址传递的方法。
# include<iostream>
using namespace std;
void Swap01(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
void Swap02(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void Swap03(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 20;
int b = 10;
// Swap01(a, b); // 值传递形参不修饰实参
// Swap02(&a, &b); // 地址传递形参可以修饰实参
Swap03(a, b); //引用传递也可以修饰实参
cout << "a = " << a << endl;
cout << "b = " << b << endl;
system("pause");
return 0;
}
引用做函数返回值
# include<iostream>
using namespace std;
// 返回局部变量引用
int& test01()
{
int a = 10; //局部变量
return a;
}
// 返回静态变量引用
int& test02()
{
static int a = 10; //静态变量,存储在全局区
return a;
}
int main()
{
int &a = test01(); // 局部变量的引用不能返回
cout << "a = " << a << endl; // a = 10,因为操作系统有保留
cout << "a = " << a << endl; // 乱码,因为栈区的内存已经释放
int& ref = test02();
cout << "ref = " << ref << endl;
cout << "ref = " << ref << endl;
// 函数返回引用时,函数调用可以作为左值存在,可以被赋值
test02() = 1000;
cout << "ref = " << ref << endl;
system("pause");
return 0;
}
引用的本质
**引用的本质是一个指针常量**
常量引用
修饰形参,防止误操作
# include<iostream>
using namespace std;
void Print(const int& a)
{
// a = 1000; // 如果不加const,会对实参进行修改;加const后就不再可以修改
cout << "a = " << a << endl;
}
int main()
{
int a = 10;
// int& b = a;
// int& c = 10; 错误,因为10是常量,引用必须指向一个合理的内存空间
// 正确,因为系统对其进行了优化为 int tmp = 10; const int &c = tmp;
// const int& c = 10;
Print(a);
system("pause");
return 0;
}
C++函数高级
函数的默认参数
就是在定义函数的时候,给形参一个默认值。
注意事项:
如果某个位置已经有了默认参数,那么从这个位置后,从左到右都必须有默认值;
函数声明和函数定义只能有一者具有默认参数。
# include<iostream>
using namespace std;
// 函数默认参数
int func(int a = 10, int b = 30)
{
return a + b;
}
int main()
{
cout << func() << endl; // 40
// 实参不传入值时候,就用默认参数,传入就用实参的值
cout << func(20) << endl; // 50
cout << func(20, 40) << endl; // 60
system("pause");
return 0;
}
函数的占位参数
语法:返回值类型 函数名 (数据类型){}
# include<iostream>
using namespace std;
// 函数占位参数
// 占位参数也可以拥有默认参数
int func(int a = 10, int)
{
return a;
}
int main()
{
func(10, 10) // 这个int位的10要传入,但是在函数体中没有用,目前用不上
system("pause");
return 0;
}
函数重载的使用方法
作用:函数名可以相同,提高复用性
函数重载需要满足条件:
-
同一个作用域下
-
函数名称相同
-
函数参数类型不同或者个数不同或者顺序不同
# include<iostream> using namespace std; // 函数重载 void func() { cout << "this is func()" << endl; } void func(int a) { cout << "this is a func()" << endl; } void func(int a, double b) { cout << "this is int a double b func()" << endl; } void func(double b, int a) { cout << "this is double b int a func()" << endl; } // 无法重载的,函数返回不能作为函数重载的条件 //int func(int a) //{ // return a; // //} int main() { func(); func(10); func(10, 3.14); func(3.14, 10); system("pause"); return 0; }
函数重载的注意事项
# include<iostream>
using namespace std;
// 函数重载注意事项
// 1、引用作为函数重载的参数
void func(int& a) // int &a = 10 是不合法的命令
{
cout << "this is & func()" << endl;
}
void func(const int& a) // const int &a = 10 是合法的命令
{
cout << "this is const & func()" << endl;
}
// 函数重载碰到默认参数
void func2(int a, int b = 10)
{
cout << "" << endl;
}
void func2(int a)
{
cout << "" << endl;
}
int main()
{
int a = 10;
func(a); // this is & func()
func(10); // this is const & func()
// func2(20); 出现二义性,不能调用函数
// 函数重载尽量避开这种默认参数
system("pause");
return 0;
}
C++类和对象—封装
封装基础知识
C++面向对象的三大特性为:封装、继承、多态
封装的意义:将属性和行为作为一个整体,表现生活中的事物;将属性和行为加以权限控制。
# include<iostream>
using namespace std;
const double PI = 3.14;
//定义一个圆类,能够返回圆的周长
class Circle
{
// 访问权限
// 公共权限
public:
// 属性,常用变量表示
int m_r;
// 行为,常用函数表示
double calculateZC()
{
return 2 * PI * m_r;
}
};
int main()
{
//实例化一个圆,通过一个类创建一个类的对象
Circle c;
c.m_r = 10;
cout << "圆的周长是:" << c.calculateZC() << endl;
system("pause");
return 0;
}
封装案例—设计学生类
# include<iostream>
using namespace std;
# include<string>
// 设计一个学生类,可以对学生的姓名和ID进行赋值,这个类也可以打印学生的姓名和ID
class Student
{
// 访问权限
// 公共权限
public:
// 类中的属性和行为,统称为成员
// 属性,常用变量表示 成员属性 成员变量
string name;
int ID;
// 行为,常用函数表示 成员函数 成员方法
void printName()
{
cout << "这个学生的姓名是:" << name << endl;
}
void printID()
{
cout << "这个学生的ID是:" << ID << endl;
}
};
int main()
{
Student s;
s.name = "张三";
s.ID = 202205;
s.printName();
s.printID();
system("pause");
return 0;
}
类和对象—封装—访问权限
类在设计时,可以把属性和行为放在不同的权限下,加以控制
public:类内可以访问,类外也可以访问; protected:类内可以访问,类外不可以访问; private:类内可以访问,类外不可以访问
# include<iostream>
using namespace std;
# include<string>
class Person
{
public:
string name;
protected:
string carID;
private:
int money;
public:
void printContent()
{
carID = "奔驰";
money = 50;
cout << "name= " << name << endl;
cout << carID << endl;
cout << money << "w" << endl;
}
};
int main()
{
Person p;
p.name = "老王"; // public可以访问
// p.carID = ""; 不可访问
// p.money = ; 不可访问
p.printContent();
system("pause");
return 0;
}
c++中class和struct的区别
struct和class的唯一区别在于默认的访问权限不同,struct默认为公有,class默认为私有。
# include<iostream>
using namespace std;
class C1
{
int num; // 默认的权限
};
struct C2
{
int num;
};
int main()
{
C1 c1;
// c1.num = 20; 默认的是私有权限不能访问
C2 c2;
c2.num = 20;
cout << c2.num << endl;
system("pause");
return 0;
}
类和对象—封装—成员属性私有化
优点:将所有成员属性设置为私有,可以自己控制读写权限;对于写权限,我们可以检测数据的有效性。
# include<iostream>
using namespace std;
# include<string>
class Person
{
private:
string p_name; //成员属性为私有,不可在class外不可直接访问
int p_age;
public:
// 让name可读可写,通过成员函数访问和修改
void set_name(string name)
{
p_name = name;
}
string get_name()
{
return p_name;
}
// 让age可读可写,但是必须在合理范围内(检测数据有效性)
int get_age()
{
return p_age;
}
void set_age(int age)
{
if (age < 10 || age > 100)
{
p_age = 0;
cout << "输入值异常!" << endl;
return;
}
p_age = age;
}
};
int main()
{
Person p;
p.set_name("张三");
p.set_age(150);
cout << "这个人的姓名是:" << p.get_name() << endl;
cout << "这个人的年龄是:" << p.get_age() << endl;
p.set_age(18);
cout << "这个人的年龄是:" << p.get_age() << endl;
system("pause");
return 0;
}
类和对象—立方体类案例
题目:设计立方体类,求出立方体的体积和面积,分别用全局函数和成员函数判断两个立方体是否相等。
# include<iostream>
using namespace std;
# include<string>
class Cube
{
private:
int m_L; //长度
int m_W; //宽度
int m_H; //高度
public:
// 设置这个立方体的长宽高
void set_cube(int length, int width, int height)
{
m_L = length;
m_W = width;
m_H = height;
}
// 获取这个立方体的长宽高
int get_cube()
{
return m_L, m_W, m_H;
}
// 计算这个立方体的面积
int calS()
{
return 2 * (m_L * m_W + m_L * m_H + m_W * m_H);
}
// 计算这个立方体的体积
int calV()
{
return m_L * m_W * m_H;
}
// 比较这个立方体和外面的立方体是否相等
bool compare2(Cube c, int c_L, int c_W, int c_H)
{
if (c_L == m_L && c_W == m_W && c_H == m_H)
{
return true;
}
return false;
}
};
// 比较两个立方体是否相等的函数
bool compare(Cube &c1, Cube &c2)
{
if (c1.get_cube()== c2.get_cube())
{
return true;
}
return false;
}
int main()
{
Cube c1;
Cube c2;
c2.set_cube(10, 10, 10);
c1.set_cube(10, 10, 10);
cout << "c1这个立方体的面积是:" << c1.calS() << endl; // 600
cout << "c2这个立方体的体积是:" << c1.calV() << endl; // 1000
// 用全局函数判断立方体是否相等
bool ret = compare(c1, c2);
if (ret)
{
cout << "全局函数识别结果是:这两个立方体相等" << endl;
}
else
cout << "全局函数识别结果是:这两个立方体不相等" << endl;
// 用成员函数判断立方体是否相等
int c2L = 0; // 初始化变量
int c2W = 0;
int c2H = 0;
c2L, c2W, c2H = c2.get_cube(); // 获取c2的长宽高
bool set = c1.compare2(c2, c2L,c2W, c2H);
if (ret)
{
cout << "成员函数识别结果是:这两个立方体相等" << endl;
}
else
cout << "成员函数识别结果是:这两个立方体不相等" << endl;
system("pause");
return 0;
}
C++类和对象—对象特性
类和对象—对象特性—构造函数和析构函数
对象的初始化和清理由构造函数和析构函数解决,如果我们不提供构造和析构,编译器会提供,但是编译器提供的构造和析构函数是空实现。
构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
语法:类名(){}
析构函数:主要作用在于对象销毁前系统统一调用,执行一些清理工作。
语法:~类名(){}
#include<iostream>
using namespace std;
// 对象的初始化和清理
//1、构造函数 进行初始化操作
class Person
{
public:
//1.1构造函数
// 没有返回值,不用写void
// 函数名与类名相同
// 构造函数可以有参数,可以发生重载
// 创建对象的时候,构造函数会自动调用,且只调用一次
Person()
{
cout << "Person构造函数的调用。" << endl;
}
// 1.2 析构函数
// 不写void,没有返回值
// 函数名和类名相同,前加~
// 析构函数不可以有参数,不可以发生重载
// 对象销毁前,会自动调用析构函数,而且只调用一次
~Person()
{
cout << "Person析构函数的调用。" << endl;
}
};
void test01()
{
Person p; // 在栈区的数据,test01执行完毕后,自动释放
}
int main()
{
test01();
Person p; // 在main函数结束之后(按了enter之后)才会调用析构函数
system("pause");
return 0;
}
类和对象—对象特性—构造函数的分类及调用
两种分类方式:
按参数分为:有参构造和无参构造
按类型分为:普通构造和拷贝构造
三种调用方式:
括号法;显示法;隐式转换法。
#include<iostream>
using namespace std;
//
// 按照参数分为有参和无参(默认构造)
// 按照类型分为普通和拷贝构造
class Person
{
public:
Person()
{
cout << "Person的无参构造函数的调用。" << endl;
}
Person(int a)
{
age = a;
cout << "Person的有参构造函数调用。" << endl;
}
Person(const Person &p)
{
// 将传入的人身上的所有属性,拷贝到自己身上
age = p.age;
cout << "Person的拷贝构造函数调用。" << endl;
}
~Person()
{
cout << "Person析构函数的调用。" << endl;
}
public:
int age;
};
void test01()
{
// 1、括号法
Person p; // 默认构造函数调用(无参)
Person p2(10); // 有参构造函数调用
Person p3(p2); // 拷贝构造函数调用
// 注意事项
// 调用默认构造函数时候,不要加()
// 因为这行代码:< Person p() >编译器会认为是一个函数声明
// 2、显示法
Person p11;
Person p22 = Person(10); // 有参
Person p33 = Person(p22); // 拷贝
// Person(10)匿名对象 特点:当前行执行后,系统会立刻收回匿名对象
// 注意事项
// 不要利用拷贝函数 初始化匿名对象,编译器会认为Person(p3) == Person p3
// 3、隐式转换法
Person p4 = 10; // 有参
Person p5 = p4; // 拷贝
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—对象特性—拷贝函数调用时机
C++中拷贝构造函数的调用时机通常由三种情况:
- 使用一个已经创建完毕的对象来初始化一个新的对象
- 值传递的方式给函数传值
- 以值方式返回局部对象
#include<iostream>
using namespace std;
// 拷贝构造函数调用时机
class Person
{
public:
Person()
{
cout << "这是默认构造函数调用" << endl;
}
Person(int a)
{
age = a;
cout << "这是有参构造函数调用" << endl;
}
Person(const Person &p)
{
age = p.age;
cout << "这是拷贝构造函数调用" << endl;
}
~Person()
{
cout << "这是析构函数调用" << endl;
}
int age;
};
// 1、使用已经创建完毕的对象来初始化一个新的对象
void test01()
{
Person p1(10);
Person p2(p1);
}
// 2、值传递的方式给函数参数传值
void Dowork(Person p)
{
}
void test02()
{
Person p;
Dowork(p);
}
// 3、值方式返回局部对象
Person dowork()
{
Person p1;
cout << (int*) & p1 << endl;
return p1;
}
void test03()
{
Person p = dowork();
cout << (int*)&p<< endl;
}
int main()
{
test01();
test02();
test03();
system("pause");
return 0;
}
这里显示并没有调用拷贝函数,弹幕说是编译器做了优化……,不过感觉无伤大雅。
类和对象—对象特性—构造函数调用规则
构造函数调用规则如下:
如果用户自定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造;
如果用户自定义拷贝构造函数,C++不会再提供其他构造函数。
#include <iostream>
using namespace std;
// 构造函数的调用规则
// 1、创建一个类编译器会提供至少三个函数,默认构造、析构函数、拷贝构造(值传递)
// 创建一个Person类
class Person
{
public:
Person()
{
cout << "这是默认构造函数。" << endl;
}
Person(int age)
{
m_age = age;
cout << "这是有参构造函数。" << endl;
}
//Person(const Person &p)
//{
// cout << "这是拷贝构造函数。" << endl;
// m_age=p.m_age;
//}
~Person()
{
cout << "这是析构函数。" << endl;
}
int m_age;
};
void test01()
{
Person p;
p.m_age = 18;
Person p2(p); //这里调用系统自带的拷贝函数
cout << p2.m_age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
#include <iostream>
using namespace std;
// 构造函数的调用规则
// 1、创建一个类编译器会提供至少三个函数,默认构造、析构函数、拷贝构造(值传递)
// 创建一个Person类
class Person
{
public:
//Person()
//{
// cout << "这是默认构造函数。" << endl;
//}
Person(int age)
{
m_age = age;
cout << "这是有参构造函数。" << endl;
}
//Person(const Person &p)
//{
// cout << "这是拷贝构造函数。" << endl;
// m_age=p.m_age;
//}
~Person()
{
cout << "这是析构函数。" << endl;
}
int m_age;
};
//void test01()
//{
// Person p;
// p.m_age = 18;
// Person p2(p);
// cout << p2.m_age << endl;
//}
void test02()
{
//Person p; 无法调用,系统不再提供默认构造
Person p(18);
Person p2(p); // 系统继续提供拷贝构造函数
}
int main()
{
// test01();
test02();
system("pause");
return 0;
}
#include <iostream>
using namespace std;
// 构造函数的调用规则
// 1、创建一个类编译器会提供至少三个函数,默认构造、析构函数、拷贝构造(值传递)
// 创建一个Person类
class Person
{
public:
//Person()
//{
// cout << "这是默认构造函数。" << endl;
//}
/*Person(int age)
{
m_age = age;
cout << "这是有参构造函数。" << endl;
}*/
Person(const Person &p)
{
cout << "这是拷贝构造函数。" << endl;
m_age=p.m_age;
}
~Person()
{
cout << "这是析构函数。" << endl;
}
int m_age;
};
//void test01()
//{
// Person p;
// p.m_age = 18;
// Person p2(p);
// cout << p2.m_age << endl;
//}
//2. 如果用户自定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造;
//void test02()
//{
// //Person p; 无法调用,系统不再提供默认构造
// Person p(18);
// Person p2(p);
//
//}
// 3. 如果用户自定义拷贝构造函数,C++不会再提供其他构造函数。
void test03()
{
// Person p; 无法调用,系统不再提供默认构造和有参构造
// Person p2(18);
}
int main()
{
// test01();
//test02();
system("pause");
return 0;
}
类和对象—对象特性—深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age,int height)
{
m_Height = new int(height); // 把数据开辟到堆区
m_age = age;
cout << "这是有参构造函数。" << endl;
}
Person(const Person& p)
{
cout << "这是拷贝构造函数。" << endl;
m_age = p.m_age;
// m_Height = p.m_Height;系统提供的拷贝构造实现的就是这行代码
//深拷贝
m_Height = new int(*p.m_Height);
}
~Person()
{
// 将堆区的数据释放
if (m_Height != NULL)
{
delete m_Height;
m_Height = NULL; //防止野指针
}
cout << "这是析构函数。" << endl;
}
int m_age;
int* m_Height;
};
void test01()
{
Person p1(18, 160);
cout << "p1的年龄是:" << p1.m_age <<"p1的身高是:"<< *p1.m_Height << endl;
Person p2(p1);
cout << "p2的年龄是:" << p2.m_age <<"p2的身高是:"<< *p2.m_Height << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—对象特性—初始化列表
作用:C++提供初始化列表语法,用来初始化属性
语法:构造函数():属性1(值1),属性2(值2)…{}
#include<iostream>
using namespace std;
class Person
{
public:
int m_a;
int m_b;
int m_c;
// 初始化列表
Person(int a,int b, int c) :m_a(a), m_b(b), m_c(c)
{
}
};
int main()
{
Person p(10, 20, 30);
cout << p.m_a<<" " << p.m_b <<" " << p.m_c << endl; // 10 20 30
cout<<
system("pause");
return 0;
}
类和对象—对象特性—类对象作为类成员
C++中允许一个类实例化的对象可以是另一个类的成员,我们称该成员为对象成员
#include<iostream>
using namespace std;
#include<string>
class Phone
{
public:
string m_pname;
Phone(string name)
{
cout << "Phone的构造函数调用。" << endl;
m_pname = name;
}
~Phone()
{
cout << "Phone的析构函数调用。" << endl;
}
};
class Person
{
public:
string m_name;
Phone m_phone;
// Phone m_phone = pname; 隐式转换法
Person(string name, string pname) :m_name(name), m_phone(pname)
{
cout << "Person的构造函数调用。" << endl;
}
~Person()
{
cout << "Person的析构函数调用。" << endl;
}
};
//构造时候先构造其他类的对象,再构造自身。析构的顺序与构造相反。
void test011()
{
Person p("张三", "苹果手机");
cout << p.m_name << "拿着:" << p.m_phone.m_pname << endl;
}
int main()
{
test011();
system("pause");
return 0;
}
C++类和对象—静态成员
静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
-
静态成员变量:
所有对象共享同一份数据
在编译阶段分配内存
类内声明,类外初始化
#include<iostream> using namespace std; class person { public: static int m_a; // 静态成员变量也是有访问权限的 private: static int m_b; }; //类外初始化 int person::m_a = 100; int person::m_b = 200; void test0111() { person p; cout << p.m_a << endl; // 100 person p2; p2.m_a = 200; cout << p2.m_a << endl; // 200 } void test0222() { //静态成员变量不属于某个对象上,所有对象都共享一份数据 //因此静态成员变量有两种访问方式 //1、通过对象进行访问 person p3; cout << p3.m_a << endl; //2、通过类名进行访问 cout << person::m_a << endl; // cout << person::m_b << endl; 错误不可访问m_b } int main() { //test0111(); test0222(); system("pause"); return 0; }
静态成员函数:
-
所有对象共享同一个函数
-
静态成员函数只能访问静态成员变量
#include<iostream> using namespace std; class person { public: static void func() { m_a = 100; //静态成员函数可以访问静态成员变量 // 静态成员函数不可以访问非静态成员变量,无法区分是哪个对象的m_b // m_b = 30; 错误,不可访问 cout << "static void func 调用" << endl; } static int m_a; int m_b; // 静态成员函数也具有访问权限 private: static void func2() { } }; //类外初始化 int person::m_a = 0; void test0111() { // 1、通过对象访问 person p; p.func(); // 2、通过类名访问 person::func(); // person::func2(); 错误的,不可访问func2 } int main() { test0111(); system("pause"); return 0; }
类和对象—对象特性—成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
#include<iostream>
using namespace std;
class APerson
{
int m_A; //非静态成员变量属于类的对象上
static int m_B; // 不属于类的对象上,不增加内存
void func() // 非静态成员函数,不属于类的对象上
{
}
static void func2() //静态成员函数,不属于类的对象上
{
}
};
int APerson::m_B = 10;
void test()
{
APerson p;
// C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
// 每个空对象也应该有一个独一无二的内存地址
// 未添加属性时,即空对象所占内存是1
cout << "size of p = " << sizeof(p) << endl; // 4
}
int main()
{
test();
system("pause");
return 0;
}
类和对象—对象特性—this指针的用途
C++this指针指向被调用的成员函数所属的对象。隐含在每个非静态函数内,无须定义,直接使用即可。
用途:
当形参和成员变量同名时,可以用this指针来区分。
在类的非静态成员函数中返回对象本身,可使用return \*this
#include<iostream>
using namespace std;
class Person
{
public:
int age;
Person(int age)
{
// this指针指向被调用的成员函数所属的对象
this->age = age; // 属性和形参名冲突,如果不加this,系统无法识别age属性
}
void Addage(Person& p)
{
this->age += p.age;
}
Person& personAddage(Person& p)
{
this->age += p.age;
return *this;
}
};
// 1、解决名称冲突
void testa()
{
Person p(18);
cout << p.age << endl; // 18
}
// 2、返回对象本身用*this
void testb()
{
Person p1(10);
Person p2(20);
p2.Addage(p1);
cout << p2.age << endl; // 30
// 链式编程思想
p2.personAddage(p1).personAddage(p1).personAddage(p1);
cout << p2.age << endl; // 60
}
int main()
{
testa();
testb();
system("pause");
return 0;
}
类和对象—对象特性—空指针访问成员函数
C++中允许空指针调用成员函数,但是也要注意有没有用到this指针,如果用到this指针,需要加以判断保证代码的健壮性。
#include<iostream>
using namespace std;
class Person
{
public:
void showClassname()
{
cout << "this is Person class. " << endl;
}
void showPersonage()
{
// 报错原因是因为传入的指针是NULL。m_age无法用一个空的指向的this得到
if (this == NULL)
{
return;
}
cout << "perspn age = " << m_age << endl;
}
int m_age;
};
void testa()
{
Person* p = NULL;
p->showClassname(); //可以运行
// p->showPersonage(); 不能运行
}
int main()
{
testa();
system("pause");
return 0;
}
类和对象—对象特性—const修饰成员函数(常函数)
常函数:
- 成员函数后加const后我们称这个函数为常函数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
- 声明对象前加const称该对象为常对象
- 常对象只能调用常函数
#include<iostream>
using namespace std;
class Person
{
public:
// this指针本质上是指针常量,不可以修改它的指向
// const Person *const this;
// 成员函数后的const本质上是修饰this,让其指向的值也不能再修改
void showPersonage() const
{
// m_age = 100; 错误的,不可以修改
m_b = 10;
}
void func()
{
}
int m_age;
mutable int m_b;
};
void testa()
{
Person p;
p.showPersonage();
cout << p.m_b << endl; // 10
}
void testb()
{
const Person p;
// p.m_age = 10; 错误不可修改
p.m_b = 20; // 在常对象也可以修改
// 常对象只能调用常函数
p.showPersonage();
// p.func(); 常对象不可以调用普通成员函数,因为普通成员函数可以修改属性,但是常对象不允许修改属性
}
int main()
{
testa();
system("pause");
return 0;
}
C++类和对象—友元
类和对象—友元—全局函数做友元
友元的目的就是让一个函数或者类访问另一个类中私有成员,关键字是friend
友元的三种实现:
全局函数做友元
类做友元
成员函数做友元
#include<iostream>
using namespace std;
#include<string>
class Building
{
// 现在visiting全局函数是Building的友元可以访问Building中的私有成员了
friend void visting(Building* building);
public:
Building()
{
m_SettingRoom = "客厅";
m_BedRoom = "卧室";
}
public:
string m_SettingRoom;
private:
string m_BedRoom;
};
//
void visting(Building *building)
{
cout << "正在访问:" << building->m_SettingRoom << endl;
cout << "正在访问:" << building->m_BedRoom << endl;
}
void test01()
{
Building building;
visting(&building);
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—友元—友元类
类做友元。
#include<iostream>
using namespace std;
#include<string>
class Building
{
friend class Friend; // Friend 可以访问Building的私有成员
public:
Building();
public:
string m_SettingRoom;
private:
string m_BedRoom;
};
class Friend
{
public:
void visit(); // 参观函数访问building中的属性
Friend();
Building* building;
};
// 在类外写函数
Building::Building()
{
m_SettingRoom = "客厅";
m_BedRoom = "卧室";
}
Friend::Friend()
{
// 创建building对象
building = new Building;
}
void Friend::visit()
{
cout << "Friend正在访问:" << building->m_SettingRoom << endl;
cout << "Friend正在访问:" << building->m_BedRoom << endl;
}
void test01()
{
Friend ff;
ff.visit();
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—友元—成员函数做友元
#include<iostream>
using namespace std;
#include<string>
class Building; //提前声明,因为Friend类中要创建Building类的指针
class Friend
{
public:
Friend();
Building* building;
void visit(); // 让其做友元函数
void visit2(); // 普通函数
};
class Building
{
friend void Friend::visit(); // visit函数必须在前面声明,因为这里再次声明其为友元函数
public:
Building();
public:
string m_SettingRoom;
private:
string m_BedRoom;
};
Building::Building()
{
m_BedRoom = "卧室";
m_SettingRoom = "客厅";
}
Friend::Friend()
{
building = new Building;
}
void Friend::visit()
{
cout << "Friend正在访问: " << building->m_SettingRoom << endl;
cout << "Friend正在访问: " << building->m_BedRoom << endl;
}
void Friend::visit2()
{
cout << "Friend正在访问: " << building->m_SettingRoom << endl;
// cout << "Friend正在访问: " << building->m_BedRoom << endl;错误的visit02()不可以访问
}
void test01()
{
Friend ff;
ff.visit();
}
int main()
{
test01();
system("pause");
return 0;
}
C++类和对象—C++运算符重载
类和对象—C++运算符重载—加号运算符重载
运算符重载概念;对已有的运算符重新进行定义,赋予另一种功能,以适应不同的数据类型。
#include<iostream>
using namespace std;
//加号运算符重载
class Person
{
public:
// 1、成员函数重载
Person operator+(Person& p)
{
Person tmp;
tmp.m_A = this->m_A + p.m_A;
tmp.m_B = this->m_B + p.m_B;
return tmp;
}
int m_A;
int m_B;
};
//2、全局函数重载
Person operator+(Person& p1, Person& p2)
{
Person tmp;
tmp.m_A = p1.m_A + p2.m_A;
tmp.m_B = p1.m_B + p2.m_B;
return tmp;
}
// 运算符重载也可以发生函数重载
Person operator+(Person& p1, int num)
{
Person tmp;
tmp.m_A = p1.m_A + num;
tmp.m_B = p1.m_B + num;
return tmp;
}
void test01()
{
Person p1;
p1.m_A = 10;
p1.m_B = 20;
Person p2;
p2.m_A = 10;
p2.m_B = 20;
Person p3 = p1 + p2;
Person p4 = p1 + 100;
cout << "p3.m_A= " << p3.m_A << " p3.m_B= " << p3.m_B << endl;
cout << "p4.m_A= " << p4.m_A << " p4.m_B= " << p4.m_B << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—C++运算符重载—左移运算符重载
作用:可以输出自定义数据类型。
#include<iostream>
using namespace std;
class Person
{
friend ostream& operator<<(ostream& cout, Person& p);
public:
Person(int a, int b)
{
m_A = a;
m_B = b;
}
public:
int m_A;
private:
int m_B;
//1、利用成员函数重载左移运算符
// void operator<<(cout)
// 无法得到cout<<形式的输出结果,因此只能利用全局函数重载左移运算符
};
// 2、利用全局函数重载左移运算符
ostream & operator<<(ostream &cout, Person &p)
{
cout << "p.m_A= " << p.m_A << " p.m_B= " << p.m_B;
return cout; // out属于ostream输出流数据类型
}
void test01()
{
Person p(20, 10);
cout << p << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—C++运算符重载—递增运算符重载
作用:通过重载递增运算符,实现自己的整型数据
#include<iostream>
using namespace std;
// ++运算符重载
class MyInteger
{
friend ostream& operator<<(ostream &cout, MyInteger myint);
public:
MyInteger()
{
m_Num = 0;
}
// 前置++
MyInteger& operator++() // 返回引用是为了对一个数据进行操作
{
//先进行++运算
m_Num++;
//再对自身进行返回
return *this;
}
// 后置++
//int 代表占位参数,可以用于区分前置和后置参数
MyInteger operator++(int) //后置递增是返回值
{
MyInteger tmp = *this;
m_Num++;
return tmp;
}
private:
int m_Num;
};
//左移运算符重载
ostream & operator<<(ostream &cout, MyInteger myint)
{
cout << myint.m_Num;
return cout;
}
void test01()
{
MyInteger myint1;
cout << ++myint1 << endl;
cout << myint1 << endl;
MyInteger myint2;
cout << myint2++ << endl;
cout << myint2 << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—C++运算符重载—赋值运算符重载
C++编译器至少给一个类添加4个函数,
默认构造函数、默认析构函数、默认拷贝构造函数、赋值运算符operator=
,对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作也会出现深浅拷贝问题。
#include <iostream>
using namespace std;
class Person
{
public:
Person(int age)
{
m_Age = new int(age);
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
//重载赋值运算符
Person& operator=(Person& p)
{
//这是编译器提供的赋值操作,编译器提供的赋值操作会出现浅拷贝问题
// m_Age = p.m_Age;
//应该先判断是否有属性在堆区,如果有先释放干净,然后再进行深拷贝
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
//深拷贝
m_Age = new int(*p.m_Age);
//返回自身,这样才能实现连等操作
return *this;
}
int *m_Age;
};
void test01()
{
Person p(18);
Person p2(20);
Person p3(30);
p = p2 = p3;
cout << "p.age= " << *p.m_Age << endl;
cout << "p2.age= " << *p2.m_Age << endl;
cout << "p3.age= " << *p3.m_Age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—C++运算符重载—关系运算符重载
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
#include<iostream>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
m_name = name;
m_age = age;
}
bool operator==(Person& p)
{
if (this->m_name == p.m_name && this->m_age == p.m_age)
{
return true;
}
return false;
}
bool operator!=(Person& p)
{
if (this->m_name == p.m_name && this->m_age == p.m_age)
{
return false;
}
return true;
}
string m_name;
int m_age;
};
void test01()
{
Person p1("Tom", 19);
Person p2("Tom", 18);
if (p1 == p2)
{
cout << "p1 = p2" << endl;
}
else
{
cout << "p1 != p2" << endl;
}
if (p1 != p2)
{
cout << "p1 != p2" << endl;
}
else
{
cout << "p1 = p2" << endl;
}
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—C++运算符重载—函数调用运算符重载
函数调用运算符()也可以重载
由于重载后使用的方式非常像函数调用,因此称为仿函数
仿函数没有固定写法,非常灵活
#include<iostream>
using namespace std;
#include<string>
class MyPrint
{
public:
//函数调用运算符重载
void operator()(string test)
{
cout << test << endl;
}
};
class MyAdd
{
public:
//函数调用运算符重载
int operator()(int num1,int num2)
{
return num1 + num2;
}
};
void test01()
{
MyPrint myprint;
myprint("hello cpp");
MyAdd myadd;
int ret = myadd(10, 20);
cout << "ret= " << ret << endl;
//匿名函数对象
cout << MyAdd()(100, 200) << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
C++类和对象—继承
类和对象—继承—基本语法
继承是面向对象三大特性之一
定义一些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。
这个时候我们就可以考虑利用继承的技术,减少重复代码。
#include<iostream>
using namespace std;
//普通实现页面,会有很多重复代码
//JAVA页面
class Java
{
public:
void header()
{
cout << "首页、公开课、登录、注册…(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图…(公共底部)" << endl;
}
void lefter()
{
cout << "JAVA、python、C++…(公共分类列表)" << endl;
}
void content()
{
cout << "Java学科视频" << endl;
}
};
class Python
{
public:
void header()
{
cout << "首页、公开课、登录、注册…(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图…(公共底部)" << endl;
}
void lefter()
{
cout << "JAVA、python、C++…(公共分类列表)" << endl;
}
void content()
{
cout << "Python学科视频" << endl;
}
};
class Cpp
{
public:
void header()
{
cout << "首页、公开课、登录、注册…(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图…(公共底部)" << endl;
}
void lefter()
{
cout << "JAVA、python、C++…(公共分类列表)" << endl;
}
void content()
{
cout << "Cpp学科视频" << endl;
}
};
void test01()
{
cout << "下载java主页: " << endl;
Java ja;
ja.header();
ja.footer();
ja.lefter();
ja.content();
cout << "******************************" << endl;
cout << "下载Python主页: " << endl;
Python pn;
pn.header();
pn.footer();
pn.lefter();
pn.content();
cout << "******************************" << endl;
cout << "下载Cpp主页: " << endl;
Cpp cp;
cp.header();
cp.footer();
cp.lefter();
cp.content();
}
int main()
{
test01();
system("pause");
return 0;
}
#include<iostream>
using namespace std;
//继承类页面实现
//继承的好处:减少重复代码
//语法:class 子类:继承方式 父类
//子类也称为派生类
//父类也成为基类
class Basepage
{
public:
void header()
{
cout << "首页、公开课、登录、注册…(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图…(公共底部)" << endl;
}
void lefter()
{
cout << "java、python、c++…(公共分类列表)" << endl;
}
};
//Jave
class Java:public Basepage
{
public:
void content()
{
cout << "Java学科视频" << endl;
}
};
//Python
class Python :public Basepage
{
public:
void content()
{
cout << "Python学科视频" << endl;
}
};
//C++
class Cpp :public Basepage
{
public:
void content()
{
cout << "C++学科视频" << endl;
}
};
void test01()
{
cout << "下载java主页: " << endl;
Java ja;
ja.header();
ja.footer();
ja.lefter();
ja.content();
cout << "******************************" << endl;
cout << "下载python主页: " << endl;
Python pn;
pn.header();
pn.footer();
pn.lefter();
pn.content();
cout << "******************************" << endl;
cout << "下载cpp主页: " << endl;
Cpp cp;
cp.header();
cp.footer();
cp.lefter();
cp.content();
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—继承—继承方式
继承语法:class 子类:继承方式 父类
继承方式一共有三种:公共继承、保护继承、私有继承
#include<iostream>
using namespace std;
//继承方式
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son1 :public Base
{
public:
void func()
{
m_A = 10; //父类中的公共权限成员,子类中依然是公共权限
m_B = 20; //父类中的保护权限成员,子类中依然是保护权限
// m_C = 10; 父类中的私有权限成员 子类访问不到
}
};
//保护继承
void test01()
{
Son1 s1;
s1.m_A = 10;
// s1.m_B = 20;到son1中,m_B是保护权限,类外访问不到
}
class Son2 :protected Base
{
public:
void func()
{
m_A = 10; //父类中的公共权限成员,子类中为保护权限
m_B = 20; //父类中的保护权限成员,子类中为保护权限
// m_C = 10; 父类中的私有权限成员 子类访问不到
}
};
void test02()
{
Son2 s2;
// s2.m_A = 10; 到son2中,m_A是保护权限,类外访问不到
// s2.m_B = 20;到son2中,m_B是保护权限,类外访问不到
}
//私有继承
class Son3 :private Base
{
public:
void func()
{
m_A = 10; //父类中的公共权限成员,子类中为私有权限
m_B = 20; //父类中的保护权限成员,子类中为私有权限
// m_C = 10; 父类中的私有权限成员 子类访问不到
}
};
void test03()
{
Son3 s3;
// s3.m_A = 10; 到son3中,m_A是私有权限,类外访问不到
// s3.m_B = 20;到son3中,m_B是私有权限,类外访问不到
}
class GrandSon3 :public Son3
{
public:
void func()
{
// m_A = 10; //父类中的私有权限成员,子类访问不到
// m_B = 20; //父类中的私有权限成员,子类访问不到
// m_C = 10; 父类中的私有权限成员 子类访问不到
}
};
int main()
{
system("pause");
return 0;
}
类和对象—继承—继承中的对象模型
问题:从父类继承过来的成员,哪些属于子类对象中?
#include <iostream>
using namespace std;
//
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son :public Base
{
public:
int m_D;
};
void test01()
{
//父类中的所有非静态成员属性都会被子类继承下去
//父类中私有成员属性,是被编译器隐藏了,因此访问不到,但是确实被继承下去了
cout << "size of Son: " << sizeof(Son) << endl; //16
}
int main()
{
test01();
system("pause");
return 0;
}
利用开发人员命令提示工具查看对象模型;跳转盘符*D:*
;跳转查看文件路径 *cd 具体路径*
;查看你命名*dir*
;*c1 \dl reportSingleClassLayout类名 文件名*
类和对象—继承—构造和析构顺序
问题:子类继承父类后,当创建子类对象,也会调用父类的构造函数,父类和子类的构造和析构顺序是谁先谁后?
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base构造函数!" << endl;
}
~Base()
{
cout << "Base析构函数!" << endl;
}
};
class Son :public Base
{
public:
Son()
{
cout << "Son构造函数!" << endl;
}
~Son()
{
cout << "Son析构函数!" << endl;
}
};
void test01()
{
//继承中的构造和析构顺序如下;
//先构造父类,再构造子类,析构的顺序与构造的顺序相反
Son s;
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—继承—同名成员处理
问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
访问子类同名成员,直接访问即可
访问父类同名成员,需要加作用域
#include <iostream>
using namespace std;
class Base
{
public:
int m_A;
Base()
{
m_A = 100;
}
void func()
{
cout << "Base_func()" << endl;
}
void func(int a)
{
cout << "有参Base_func()" << endl;
}
};
class Son :public Base
{
public:
int m_A;
Son()
{
m_A = 200;
}
void func()
{
cout << "Son_func()" << endl;
}
};
// 同名成员属性
void test01()
{
Son s;
cout << s.m_A << endl; //200
cout << s.Base::m_A << endl; //100
}
//同名成员函数
void test02()
{
Son s;
s.func(); //Son_func()
s.Base::func();//Base_func()
//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名函数
// 如果想访问到父类中被隐藏的同名成员函数,需要加作用域
// s.func(100); 错误,访问不到父类
s.Base::func(100);
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
类和对象—继承—同名静态成员处理
问题:继承中同名的静态成员在子类对象上如何进行访问?
静态成员和非静态成员出现同名,处理方式一致。
访问子类同名成员,直接访问即可;
访问父类同名成员,需要加作用域。
#include<iostream>
using namespace std;
class Base
{
public:
static int m_A;
static void func()
{
cout << "Base static func()" << endl;
}
};
int Base::m_A = 100;
class Son:public Base
{
public:
static int m_A;
static void func()
{
cout << "Son static func()" << endl;
}
};
int Son::m_A = 200;
// 同名静态成员属性
void test01()
{
//1、通过对象访问
Son s;
cout << s.m_A << endl; //200
cout << s.Base::m_A << endl; //100
//2、通过类名访问
cout << Son::m_A << endl; //200
//第一个::代表通过类名访问,第二个::代表访问父类作用域下
cout << Son::Base::m_A << endl; //100
}
// 同名静态成员函数
void test02()
{
//1、通过对象访问
Son s;
s.func(); //Son static func()
s.Base::func(); //Base static func()
//2、通过类名访问
Son::func(); //Son static func()
Son::Base::func(); //Base static func()
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
总结:同名静态成员处理方式和非静态处理一样,只不过有两种访问方式(通过对象和通过类名)
类和对象—继承—继承语法
C++允许一个类继承多个类
语法:class子类:继承方式 父类1,继承方式 父类2…
多继承可能会引起父类中有同名成员出现,需要加作用域区分
#include<iostream>
using namespace std;
class Base1
{
public:
int m_A;
Base1()
{
m_A = 100;
}
};
class Base2
{
public:
int m_A;
Base2()
{
m_A = 200;
}
};
//子类,需要继承两个父类
class Son :public Base1, public Base2
{
public:
Son()
{
m_C = 300;
m_D = 400;
}
int m_C;
int m_D;
};
void test01()
{
Son s;
cout << "sizeof Son = " << sizeof(s) << endl; //16
cout << "m_A= " << s.Base1::m_A << endl; //同名要加作用域
cout << "m_A= " << s.Base2::m_A << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
类和对象—继承—菱形继承问题以及解决方法
概念:两个派生类继承同一个基类,又有某个类同时继承着两个派生类,这种继承被称为菱形继承,或者钻石继承。
遇到的问题:二义性;同样属性的数据没必要开辟两个内存空间。
#include<iostream>
using namespace std;
class Animal
{
public:
int m_age;
};
// 利用虚继承可以解决菱形继承的问题
class Sheep:virtual public Animal{};
class Tuo:virtual public Animal{};
class SheepTuo:public Sheep,public Tuo{};
void test01()
{
SheepTuo st;
// st.m_age=18;错误不明确
st.Sheep::m_age = 18;
st.Tuo::m_age = 10;
cout << "st.Sheep::m_age= " << st.Sheep::m_age << endl;
cout << "st.Tuo::m_age= " << st.Tuo::m_age << endl;
//这份数据只要一份就行,菱形继承导致了资源浪费
//加了virtual后m_age成为共享的内存
}
int main()
{
test01();
system("pause");
return 0;
}
C++类和对象—多态
类和对象—多态—多态的基本语法
多态分为静态多态(函数重载和运算符重载)和动态多态(派生类和虚函数实现运行时多态),二者区别:
- 静态多态的函数地址早绑定—编译阶段确定函数地址
- 动态多态的函数地址晚绑定—运行阶段确定函数地址
#include <iostream>
using namespace std;
class Animal {
public:
// 虚函数
virtual void speak() {
cout << "Animal speaking" << endl;
}
};
class Cat :public Animal {
public:
void speak() {
cout << "Cat speaking" << endl;
}
};
// 地址早绑定,在编译阶段确定函数地址
// 动态多态满足条件
// 1、继承关系
// 2、子类要重写(函数返回值、函数名、参数列表完全相同)父类中的虚函数
// 父类的指针指向子类的对象
void Dospeak(Animal& animal) {
animal.speak();
}
void test01() {
Cat cat;
Dospeak(cat);
}
int main()
{
test01();
system("pause");
return 0;
}