C++

一、C++基础

1.c++代码框架解析

  1. #include <iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. cout << "Hello world!" << endl;
  6. //把一个字符串插入到了输出流中
  7. return 0;
  8. }

iostream:包含了关于输入输出语句的函数

  • input==(i)==:输入
  • output==(o)==:输出

关于头文件的引入:

  • 可以用原来c语言的语法:#include
  • 可以用c++的语法:#include

    • 去点==.h然后在前面加上c==就行

using namespace std:使用命名空间std(标准命名空间)

  • 命名空间为了避免起名冲突,比如:葬爱-王者,葬爱-小雨

cout << "Hello world!" << endl:

  • cout:输出流
  • <<:表示把hello World发送给cout打印
  • endl:可以理解为回车换行(相当于\n)

endl\n的区别:

  • \n:单纯回车换行
  • endl具有两重功能:

    • 换行
    • fflush(stdin):清空缓存

      • 刷新输出流:将缓冲区的数据全部传递到输出设备并将输出缓冲区清空。

二、变量、数据类型及运算符

变量:就是一个数据所占的内存空间

1.变量命名

  • 为什么要变量命名:通过变量命名可以简单快速找到内存中存储的数据(在一定范围内)
  • 如何避免重名:在C++中使用namespace命名空间技术
  • 命名规则:

    • 开头只能是字母或者下划线(不能以数字开头哦))
    • 只能由==_、字母、数字==这三个元素组成
    • 不能是关键字(保留字)

2.数据类型

  • 数值

    • 整型

      • int(32bit)
      • short(16bit)
      • long和 l ong long
    • 浮点型

      • float
      • double和long double(64bit)
  • 非数值

    • string
    • char(字符型)8bit

size _ t == unsigned int

  1. int main()
  2. {
  3. typedef char wode_char;
  4. //将char类型重命名为wode_char,就可以用wode_char来定义短整型啦
  5. wode_char name = 'hello world';
  6. cout << name << endl;
  7. return 0;
  8. }

3.使用变量的格式

  • step1:声明变量
  • step2:初始化变量(给变量第一次赋值)
  1. //声明
  2. int score_totle;
  3. //初始化(赋值)
  4. score_totle = 10

注意

  • 变量命名时名字不要重复
  • 一条语句可以声明多个类型相同的变量

输出打印

  1. #include <iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. int salary = 2500;
  6. //打印月薪
  7. cout << “小明的月薪是:” << salary << endl;
  8. //解释:将salary插入到他前面的输入输出流里面一起输出
  9. }
  1. #include <iostream>
  2. #include <cmath>
  3. using namespace std;
  4. int main()
  5. {
  6. //一直圆柱体的半径和高,求圆柱体的体积
  7. const float PI = 3.14;//定义了一个PI常量(常量是不能被改变的)
  8. float radius = 4.5;
  9. float height = 90.0f;
  10. double volume = PI * pow(redius, 2) * height;
  11. cout << "体积是: " << vloume << endl;
  12. return 0
  13. }

4.如何控制cout显示的精度

注意:C++默认展示6位有效数字

  • step1.导入头文件 #include <iomanip>
  • step1.让浮点数以小数的方式显示(不用科学计数 法来表示)

    • cout << fixed;
  • step3.控制显示的精度

    • cout << setprecision(2)
    • 精确到小数点后两位
  • cout << fixed << setprecision(2)可以把两句代码连在一起写哦

5.cin

功能:C++中的标准输出对象,从标准输入

当我们从键盘输入字符串的时候,需要敲一下回车键才能够将这个字符串送入到缓冲区中,

那么敲入的这个回车键(\r)会被转换为一个换行符\n,

这个换行符\n也会被存储在cin的缓冲区中,并且被当成一个字符来计算!

举例:比如我们在键盘上敲下了123456这个字符串,然后敲一下回车键(\r),将这个字符串送入了缓冲区中,那么此时缓冲区中的字节个数是7 ,而不是6。

  1. #include <iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. char a;
  6. int b;
  7. float c;
  8. string
  9. cin>> a >> b >> c;
  10. cout << a << " " << b << " " << c << " " << endl;
  11. system("pause")
  12. return 0;
  13. }

6.字符型变量

char

  • 字节位数:8位(bit)
  • 空间:一个字节Byte
  • 取值范围:-128 ~ +127

输入字符时建议使用:

cin.get()函数

1.setw()函数:设置宽度

  1. int main()
  2. {
  3. double a1 = 123;
  4. double a2 = 234;
  5. double a3 = 345;
  6. cout << left;//对齐方式设置为左对齐,默认为右对齐
  7. cout << setfill('-')//设置空白处的填充为'-',默认填充为空格
  8. cout << setw(8) << a1 <<
  9. setw(8) << a2 <<
  10. setw(8) << a3 << endl;
  11. }

2.i++和—i

  1. int main()
  2. {
  3. int num1 = -5, num2 = 2;
  4. num1 = num2++ - --num1;
  5. /*运算流程
  6. ①--num2=> num2 = num2 - 1]
  7. 此时连个num2的值都是1,num2 - num2 = 0 == num1
  8. ②计算num2++
  9. num2 = num2 + 1
  10. num2进行了加一减一操作,最后结果还是2
  11. */
  12. cout << num1 << '\t' << num2 << endl;
  13. }

三、表达式和条件结构

表达式:符号与操作数的组合

1.赋值运算符

①单等号 =====

计算顺序:从右往左

i += 1B站C  基础学习笔记 - 图1 i = i + 1

i = 1B站C  基础学习笔记 - 图2 i = i 1

2.关系运算符

><==

>=<=!=

  1. int main()
  2. {
  3. cout << boolalpha;//输出的时候True不会输出1而会输出True
  4. cout << "15 > 35吗?" << (15 > 35) << endl;
  5. cout << "15 < 25吗?" << (15 < 35) << endl;
  6. return 0;
  7. }

3.逻辑运算符

运算符 表达式 说明
&& 并且 条件1 && 条件2 只有两个条件都是真时最后的结果才是真
|| 条件1 || 条件2 两个条件只要有一个条件成立就成立
! ! 条件 就是取反的意思
真变成假
假变成真

4.位运算符

  • 将数字转化为2进制,然后再进行运算

B站C  基础学习笔记 - 图3

  • 负数二进制转化为10进制的方法

    • 将负数二进制取反
    • 取反后加一
    • 转化为10进制
    • 加上负号
  • 举例11111101

    • 取反:00000010
    • 加一:00000011
    • 转换:3
    • 加负号:-3

5.sizeof运算符

  • 获得数据类型所占用的内存空间的大小
  • 结果以字节为单位==(1Byte == 8bit)==

运算符优先级:

  • 单目运算符 > 算数运算符 > 逻辑运算符 > 赋值运算符

三元运算符

  1. int num = 5 > 6 ? 10 : 12;
  2. //判断如果5>6为真就会返回10
  3. //如果5>6为假,就会返回12

6.条件结构

  1. if(条件为真)
  2. {
  3. //代码块1
  4. }
  5. else
  6. {
  7. //代码块2
  8. }
  1. int main()
  2. {
  3. //使用程序判断用户输入的字符是否是合法的硬盘盘符
  4. char pan = '\0'//默认设置为空字符
  5. cout << "请输入一个字符,我来判断是否合法";
  6. cin >> pan;
  7. //pan 的范围在'A'~'Z'之间
  8. if(pan >= 'A' && pan <= 'Z')
  9. {
  10. cout << "这是合法的盘符" << endl;
  11. }
  12. else
  13. {
  14. cout << "这是不合法的盘符,请重新输入" << endl;
  15. }
  16. }
  • 初学时,能定义多少变量,就定义多少变量

7.多重if结构

  1. int main()
  2. {
  3. double flowerPrice;//花的单价
  4. //打印剧情
  5. cout <<"黎明前的黑暗渐渐褪去,海天之间透露着一抹亮光,像是点燃的火把......" << endl;
  6. cout << "小男生给小女生送花,小女生问:这花多钱吖" << endl;
  7. cout << "小男生:";
  8. cin >> flowerPrice;
  9. if(flowPrice >= 5000){
  10. cout << "直接去领证" << endl;
  11. }else if(flowPrice <5000 && flowPrice >=3000){
  12. cout << "今天去heiheihei" << endl;
  13. }else{
  14. cout << "拜拜" << endl;
  15. }
  16. return 0;
  17. }

8.switch结构

  1. switch(表达式)
  2. {
  3. case 常量1:
  4. 语句1;
  5. break;
  6. case 常量2:
  7. 语句1;
  8. break;
  9. ......
  10. default:
  11. 语句;
  12. }
  • switch:swithch后的表达式只能是整型或字符型(不能是变量)
  • case:

    • case后的表达式的值不能相同
    • case后允许多条语句,不需要大括号
    • 如果不添加break语句,需要特别注意执行顺序
  • default:

    • case和default句子的先后顺序可以自行变动
    • default可以省略
  • break:

    • 跳出,退出这个结构
    • 如果有多个case语句,中间没有break语句,那么就会依次挨着执行这些case语句
  1. int main()
  2. {
  3. int choice;
  4. cin >> choice;
  5. switch(choice){
  6. case 1:
  7. cout << "我选择老师" << endl;
  8. case 2:
  9. cout << "我选择程序员" << endl;
  10. case 3:
  11. cout << "12345" << endl;
  12. default:
  13. cout << "666" << endl;
  14. }

四、while和do…while

1.while循

控制循环的次数

循环中,通过控制变量来控制循环的次数

循环三要素

  • 循环变量的初值

    • 在循环外自定义一个变量作为循环变量初值
  • 循环变量的判断

    • while括号里面左判断,如果成立(返回True),循环继续
  • 循环变量的更新

    • 每次循环让循环变量改变(可能是自加,也可能是自减或者别的)

while循环特点:先判断,再执行

  1. int main{
  2. int i = 1;//循环变量的初值
  3. while(i <= 10){//循环变量的判断
  4. cout << "小人本住在苏州城边..\t" << i << "遍\n";
  5. i ++;//循环变量的更新
  6. }
  7. return 0;
  8. }
  9. /*
  10. while(循环条件){
  11. 循环操作语句
  12. }
  13. */

2.while循环练习题

  • 使用循环计算1-100的累加

    • 需要循环变量
    • 需要累加变量
  1. int main()
  2. {
  3. int a = 0;//定于循环变量
  4. int sum = 0;//定于总量
  5. while(a < 100){
  6. sum = sum + a;
  7. a ++;
  8. cout << a <<endl;
  9. }
  10. //输出
  11. cout << sum << endl;
  12. return 0;
  13. }
  • 使用循环实现三次密码输入错误退出系统
  1. #include <cstdlib>
  2. using namespace std;
  3. int main()
  4. {
  5. //先设置密码
  6. string passward;//密码
  7. cout << "请输入密码:" << endl;
  8. cin >> passward;
  9. int i = 0;//循环变量控制循环次数
  10. while(i < 3){
  11. cout << "请输入密码:" << endl;
  12. cin >> passward;
  13. i ++;
  14. if(passward != "12345678" and i ==3){
  15. cout << "3次输入次数已经到了,系统强制退出"<< endl;
  16. }
  17. i ++;
  18. }
  19. return 0;
  20. }
  • 某宝双十一2015年交易金额为800亿,每年递增25%
  • 问:那一年交易额达到2000亿?
  1. int main()
  2. {
  3. //总额初始为800
  4. double sum = 800;
  5. //增幅
  6. double add = 1.25;
  7. //定义循环变量控制循环次数
  8. int i = 2015;
  9. while(sum < 2000){
  10. sum = sum * add;
  11. i ++;
  12. }
  13. cout << "到"<< i <<"年的时候营业额达到了"<< sum << "亿元" << endl;
  14. return 0;
  15. }

3.codeblock调试C程序

调试流程:

  • 分析错误
  • 设置断点
  • 启动调试
  • 单步运行
  • 观察变量
  • 发现问题
  • 修正代码重新运行

4.do-while循环

  1. do{
  2. 循环操作
  3. }while(循环条件);

特点:先执行,再判断

  • 先执行一次循环操作==(无论如何循环体都会执行一次)==
  • 再判断:如果符合循环条件,继续执行循环操作
  • 不符合循环条件:退出

五、for循环

  1. for(表达式1;表达式2;表达式3)
  2. {
  3. 循环语句;
  4. }
  5. //代码举例子
  6. const int N = 20;//常量
  7. for(int i = 0;i < N;i ++)
  8. {
  9. cout << "for循环也不难" << endl;
  10. }
  11. //上面的代码和下面这个代码等价
  12. const int N = 20;
  13. while(i < N){
  14. cout << "for循环也不难" << endl;
  15. i ++;
  16. }

三个表达式:

  • ==表达式1:==通常是给循环变量赋初值,可以省略(例如:i = 0)
  • ==表达式2:==循环条件,是否继续执行循环,可以省略 (例如:i < 10)
  • ==表达式3:==更新循环变量的值,可以省略 (例如:i ++)

计算平均数:

  1. int main()
  2. {
  3. //输入六个数字,计算这六个数字的平均值
  4. double num = 0;//初始化要输入的数字
  5. double sum = 0;
  6. double average = 0;
  7. for(int i = 0;i < 6;i ++){
  8. cout << "请输入第" << i + 1 << "月的工资";
  9. cin >> num;
  10. sum += num;
  11. }
  12. //计算平均数
  13. average = sum / 6;
  14. cout << "平均工资为:" << average << endl;
  15. cout << "工资总数为:" << sum << endl;
  16. return 0;
  17. }

使用循环打印1997年7月的月历

  • 已知:1997年7月1日(星期二),香港回归
  1. int main()
  2. {
  3. //定义变量
  4. int day = 31;//7月共有31天
  5. int dayOfWeek = 2;//7月第一天是周二
  6. cout << "一\t二\t三\t四\t五\t六\t日" << endl;
  7. //先打印七月的第一天
  8. for(int i = 0; i < dayOfWeek - 1; i ++){
  9. cout << '\t';//不写endl
  10. }
  11. for(int i = 1;i <= 31 ;i ++){
  12. cout << i;
  13. if((i + dayOfWeek - 1) % 7 == 0){
  14. cout << '\n';//不写endl
  15. }else{
  16. cout << '\t';//不写endl
  17. }
  18. }
  19. //在最后再写endl
  20. //endl的刷新作用:将两个endl之间(缓冲区)的所有内容全部展示到显示器上,并清空缓冲区
  21. cout << endl;
  22. return 0;
  23. }

1.break语句

break语句作用:跳出本层循环(不是本次循环哦),执行循环以后的语句

  1. int main(){
  2. for(int i = 0;i < 10; i++){
  3. cout << "i的值:"<< i << '\n';
  4. for(int n = 10; n >= 10 && n <20; n++){
  5. cout << "n的值:" << n <<'\n';
  6. if(n == 15){
  7. break;
  8. }
  9. }
  10. }
  11. cout << endl;
  12. return 0;
  13. }

2.continue语句

continue语句的作用:跳过本次循环,继续下次循环

3.循环结构总结

  • 外层循环控制行数==(行数,换行)==
  • 内层循环控制列数==(列数,列的图形)==
  1. for(int i = 0; i < 10; i++){
  2. for(int j = 0; j < 5; j++){
  3. //控制列数,且控制列的图形
  4. cout << "※"
  5. }
  6. //控制行数
  7. cout << endl;
  8. }
  • 打印三角形:讨论 ij的关系
  1. int main(){
  2. //打印三角形的上半部分
  3. for(int i = 0; j < 4;i++){
  4. for(int j =0; j <= 2 -i; j++)
  5. {
  6. cout << " ";
  7. }
  8. for(int j = 0;j <= 2*i; j++){
  9. //cout << (char)('A' + i)
  10. //用上面这个代码可以打印出ABCD
  11. /**
  12. A
  13. BBB
  14. CCCCC
  15. DDDDDDD
  16. EEEEE
  17. FFF
  18. G
  19. **/
  20. cout << "*"
  21. }
  22. cout<< endl;
  23. }
  24. //打印三角形的下半部分
  25. for(int i = 0; j < 4;i++){
  26. for(int j =0; j <= i; j++)
  27. {
  28. cout << " ";
  29. }
  30. for(int j = 0;j <= 4 - 2*i; j++){
  31. cout << "*"
  32. }
  33. cout<< endl;
  34. }
  35. return 0;
  36. }

六、数组及常用算法

数组结构和基本要素

  • 标识符:数组名称,用于区分不同数组
  • 数组元素:向数组中存放的数据
  • 元素下标:向数组中存放数据
  • 元素下标:对数组元素进行编号
  • 元素类型:数组元素的数据类型

数组特点

  • 数组只有一个标识符(数组名)
  • 下标从0开始
  • 每个元素都通过下标来访问
  • 数组长度固定不变,避免数组越界

1.几种常见定义数组的方式

  1. int num[] = {123,23,45,'c'}
  2. //'c'是字符类型(char),在c和c++中用整型来表示
  3. //定义数组的语法
  4. //数组类型 数组名[数组大小]
  5. //这里数组大小指的是:数组内元素的个数
  6. //数组大小可以是变量
  7. int nums[25];
  8. //1.后面元素个数与声明的个数一样(合法)
  9. int years[6] = {1,2,3,4,5,6}
  10. //2.后面元素的个数小于声明的个数(合法,注意,不能大于声明的个数)
  11. int month[12] = {1,2,3}
  12. //3.不声明元素个数(计算机会自动计算数组的大小)
  13. int days[] = {1,15}
  14. //4.省略等于号也是可以的
  15. int days[]{1,2}
  16. //5.大括号里面可以位空,但是声明元素个数不能为空,代表这个列表里的所有元素都为空
  17. int days[100]{}
  18. //5.这样不行
  19. int days[] = {}

2.一维数组的动态赋值

  1. //动态地从键盘录入信息并复制
  2. const int N = 5;
  3. int nums[N];
  4. //数组长度
  5. for(int i = 0; i < N; i++){
  6. cout <<"请输入第"<< (i+1) <<"个数组元素";
  7. cin >> nums[i];
  8. }
  9. for(int i = 0; i < N; i++){
  10. cout << nums[i] << endl;
  11. }
  12. cout << "数组的大小:" << sizeof(nums) /sizeof(int) <<endl;
  13. //数组中元素个数的求法
  14. //这种方法对string类型无效
  15. cout << "数组的大小" << sizeof(数组名) / sizeof(数组元素的数据类型) << endl;

3.一个小练习

有一个数列:8,4,2,1,23,344,12

  • 循环输出数列的值
  • 求数列中所有数值的和及平均值
  1. int main(){
  2. //c++11语法中可以不用=
  3. int nums[]{8,4,2,1,23,344,12};
  4. int numsLen = sizeof(nums) / sizeof(ing);//计算出数组的长度
  5. for(int i = 0; i < numsLen; i++){
  6. cout << nums[i] <<'\t';
  7. }
  8. cout << endl;
  9. //累加操作
  10. for(int i = 0; i < numslen; i++){
  11. sum += nums[i];
  12. }
  13. //'\t'为制表符
  14. cout << "数列的和为:" << sum << "\t平均值为:" << sum / numsLen <<endl;
  15. }

4.数组的应用实例

  • 求数组中的最大值和最小值
  • 定义一个整型数组,复制后求出奇书个数和偶数个数
  1. int main(){
  2. int nums[] = {8,4,2,1,23,344,12}
  3. int numLen = sizeof(nums)/sizeof(int);
  4. int jishu=0,oushu=0,i=0;
  5. while(i<numlen){
  6. if(nums[i]%2==1){
  7. i++;
  8. jishu++;
  9. }else{
  10. i++;
  11. oushu++;
  12. }
  13. }
  14. cout << "奇数个数为:" << jishu << endl;
  15. cout << "偶数个数为:" << oushu << endl;
  16. }
  • 查找输入的数字在数组中的下标,没有找到泽下标为-1
  1. int main(){
  2. int nums[] = {8,4,2,1,23,344,12}
  3. int numLen = sizeof(nums)/sizeof(int);
  4. int searchNum; //用户要查找的数字
  5. int searchIndex = -1; //数字的下标
  6. cout << "请输入要查找的数字";
  7. cin >> searchNum;
  8. //使用循环查找
  9. for(int i = 0; i < numsLenl; i++){
  10. if(nums[i] == searchNum){
  11. searchIndex = i;//记录下来下标
  12. break;
  13. }
  14. }
  15. if(searchIndex == -1)
  16. {
  17. cout<<"数组中没有这个数字"<<endl;
  18. }else{
  19. cout << searchNum<<"在数组中的下标为"<<searchIndex<<endl;
  20. }
  21. }

5.数组排序

5.1冒泡排序

  • 第一轮比较的次数:数组长度-1
  • 下一轮比上一轮比较的次数:少一次
  • 每一趟确定一个最小的数,并将这个最小的数放在列表最后
  • 下一趟比较的次数比上一趟比较的次数少一次

    • 因为已经确定了最小的数了,刚才确定过的数就不用再次比较了
  1. //使用冒泡排序,将列表由大到小排列
  2. int temp;//设置临时变量
  3. int nums[] = {12,45,23,56,7,43,2};
  4. //外层循环控制每轮的比较的交换
  5. for(int i = 0; i < numLen; i++){
  6. for(int j = 0; j<numLen-i; j++){
  7. if(nums[j]<nums[j+1]){
  8. temp = nums[j];
  9. nums[j] = nums[j+1];
  10. nums[j+1]=temp;
  11. }
  12. }
  13. }
  14. for(int i = 0;i<numLen;i++){
  15. cout<<nums[i]<<endl;
  16. }

5.2选择排序

  • step1:从数组中选出最小的数据,将其和第一个位置的数据交换
  • setp2:接着从剩下的n-1个数据中选择次小的1个元素,与第二个元素进行交换
  • step3:不断重复,知道穷尽列表
  1. int main(){
  2. //选择排序
  3. int nums[] {8, 4, 2, 1, 23, 23, 344, 12};
  4. int numLen = sizeof(nums)/sizeof(int);
  5. //擂台变量
  6. int min = nums[0];//假设最小值是数组的第一个元素
  7. int minIndex = 0;//最小值的初始下标设置为0
  8. int temp;//临时变量用来交换
  9. for(int i = 0; i < numsLen; i++){
  10. min = nums[i];//假设第i个元素是最小值了
  11. minIndex = i;
  12. for(int j = i + 1;j < numsLen;j++){
  13. //打擂台
  14. if(nums[j] < min){
  15. min = nums[j];
  16. minIndex = j;
  17. }
  18. }
  19. //交换
  20. if(minIndex > i){
  21. temp = nums[minIndex];
  22. nums[minIndex] = nums[i];
  23. nums[i] = temp;
  24. }
  25. }
  26. cout << "排序后:" << endl;
  27. }

6.数组元素的删除和插入

数组大小一旦确定,就不能再更改啦!

  1. int main(){
  2. //对四个数字进行排序
  3. double power[99];
  4. int powerCount = 0;//这是当前数组中的元素个数
  5. double insPower;//要插入的攻击力数值
  6. int insertIndex = 0;//默认插入到第一个元素的位置
  7. power[powerCount++]=45771;//++放在后面,先执行复制,再执行自加运算
  8. power[powerCount++]=42322;
  9. power[powerCount++]=40907;
  10. power[powerCount++]=40706;
  11. //用冒泡排序进行排序
  12. double temp;
  13. for(int i = 0; i < powerCount; i++){
  14. for(int j = 0; j < powerCount - i - 1++){
  15. if(power[j] < power[j+1]){
  16. temp = power[j];
  17. power[j] = power[j+1];
  18. power[j+1] = temp;
  19. }
  20. }
  21. }
  22. //插入元素
  23. cout << "请输入要插入的数字:";
  24. cin >> insPoser;//插入以后还要保证其是有顺序的
  25. /*插入三步走:
  26. 1,找到第一个比插入数字大的位置insertIndex(先找到要插入的位置)
  27. 2,将后面的元素依次往后移动一个位置(给要插入的元素空出来一个位置)
  28. 3,将要插入的数字赋值给下标为insertIndex的元素(元素赋值)
  29. **/
  30. insertIndex = powerCount;
  31. //step1:找到第一个比插入数字大的位置insertIndex(先找到要插入的位置)
  32. for(int i = 0; i < powerCount; i++){
  33. if(insertPower > power[i]){
  34. insertIndex = i;
  35. break;
  36. }
  37. }
  38. //step2:将后面的元素依次往后移动一个位置(给要插入的元素空出来一个位置)
  39. for(int i = powerCount - 1; i >= insertIndex; i--){
  40. power[i+1] = power[i]
  41. }
  42. //step3:将要插入的数字赋值给下标为insertIndex的元素(元素赋值)
  43. power[insertIndex] = insertPower;
  44. powerCount++;
  45. /*删除三步走:
  46. 1.找到要删除的元素下标
  47. 2.把下标后面的元素都往前移动一个单位
  48. 3.总长度-1
  49. */
  50. //step1:找到要删除的元素下标
  51. for(int i = 0; i < powerCount;i++){
  52. if(insertPower>power[i]){
  53. insertIndex = i;
  54. break;
  55. }
  56. }
  57. //step2:把下标后面的元素都往前移动一个单位
  58. if(deleteIndex == INT_MIN){
  59. cout << "没有找到要删除的元素,删除失败!"<< endl;
  60. }else{
  61. for(int i = deleteIndex; i < powerCount;i++){
  62. power[i] = power[i+1];
  63. }
  64. }
  65. //step3:总长度-1
  66. powerCount--;
  67. }

7.二维数组

  1. int main(){
  2. //使用二维数组
  3. string stu_names[] {"张三","李四","王五"};
  4. string course_names[] {"语文","数学","英语"};
  5. //定义二维数组的宽和高
  6. const int ROW = 3;
  7. const int COL = 3;
  8. //定义二维数组
  9. double scores[ROW][COL];
  10. for(int i = 0;i < ROW; i++){
  11. for(int j = 0;j < COL;j++){
  12. cout << stu_names[i] <<"的"<<course_names[j]<<"成绩是:";
  13. cin >> score[i][j]
  14. }
  15. }
  16. //打印结果
  17. //step1:先打印第一排的科目(最前面要空出来一格)
  18. cout << "\t";
  19. for(int i = 0;i<COL;i++){
  20. cout<<course_names[i]<<"\t";
  21. }
  22. cout<<endl;
  23. //step2:打印后面的内容,成绩+姓名
  24. for(int i = o;i<ROW;i++){
  25. //外层循环控制列
  26. //内层循环控制行
  27. cout<<stu_name[i]<<"\t";
  28. for(int j = 0;j<COL;j++){
  29. cout << scores[i][j]<<"\t";
  30. }
  31. cout<<endl;
  32. }
  33. }

8.数组的替代品

8.1 向量容器vector

  • 动态数组,可以在运行阶段设置长度
  • 具有数组的快速索引方式
  • 可以插入和删除元素

8.2 定义和初始化

  1. #include <vector>
  2. vector<double> vec1;//常用
  3. vector<string> vec2(5);
  4. vector<数据类型> 名称(共有几个元素, 一个元素占多大空间);
  5. vector<int> vec3(20,998);
  6. //名叫vec3的容器,分配20个元素,每个元素占空间大小为998Byte

常用的函数

B站C  基础学习笔记 - 图4

  1. #include <vector>
  2. #include <>
  3. int main(){
  4. vector<bouble> vecDouble {98.7, 23.5,34.6,34.6};
  5. //向容器(数组)中插入数字
  6. vecDFouble.push_back(100.8);//在容器尾部插入一个数字
  7. //集合的通用便利方法:使用迭代器 iterator
  8. //迭代器的基本用法
  9. vector<double>::iterator it;//得到迭代器对象
  10. //it.begin从第一个元素开始迭代
  11. for(it = vecDouble.begin();it != vecDouble.end(); ++it){
  12. cout << *it << endl;
  13. }
  14. //排序
  15. sort(vecDouble.bergin(),vecDouble.end())
  16. }

七、指针基础

指针简介:指针是一个值为内存地址的变量(或数据对象)。

  1. 简言之,指针和intdouble等的变量类似,也是一个变量,但是指针里面保存的内容是内存地址。

B站C  基础学习笔记 - 图5

当ptr_year保存year的地址的时候,我们就收ptr_year指向了year

1.基本用法

数据类型 * 指针变量名;

  1. int * ptr_num;
  2. char * ptr_num;
  3. float * money_ptr;
  4. double * p_price;
  5. int num = 10;
  6. ptr_num = &num;

注意:

  • int* p的写法偏向于地址,即p就是一个地址变量,表示一个十六进制地址

  • int p的写法偏向于值。
    p是一个整型变量,能够表示一个整型值(建议两者相结合进行理解)

  • 声明中的*h和使用中的*的含义完全不一样

    • 在声明语句中,表示指针类型
    • 非声明语句中,*表示取值,取这个指针变量中保存的地址所对应的值

2.使用实例

间接运算符:*

  1. int num = 1023;
  2. int * ptr_num;
  3. ptr_num = &num;
  4. *ptr_num = 1111;
  5. //*在非定义语句中代表取值,取这个指针变量中保存的地址所对应的值
  1. int mian(){
  2. double num = 1024.5;
  3. //声明一个指针,指向num变量
  4. double * ptr_num = &num;
  5. cout << "ptr_num的值" << ptr_num << endl;
  6. cout << "ptr_num指向空间的值是:" << *ptr_num << endl;
  7. return 0;
  8. }

3.常见指针类型

3.1 空指针()

定义:空指针不指向任何对象,在试图使用一个指针之前可以首先检查是否为空

用法:

  1. int * ptr1 = nullptr;//等价于int * ptr1 = 0;
  2. int * ptr2 = 0;//直接将ptr2初始化为字面常量0
  3. #include cstdlib
  4. int * ptr3 = NULL;//等价于int * ptr3 = 0;

注意:出现指针必须要初始化,不要出现没有赋值的指针(野指针),尽量等定义了对象之后再定义指向它的指针

  1. 或者先给指针一个地址(空地址也行),再在语句中使用指针。

3.2 void *指针

一种个数 的指针类型,可以存放任意对象的地址

注意:

  • void *指针存放一个内存地址,地址指向的内容是什么类型不能确定
  • void*类型指针一般用来:

    • 拿和别的指针比较
    • 作为函数的输入和输出
    • 赋值给另一个void *指针

若指针已申明为指向某种类型数据的地址,则它不能用于存储其他类型数据的地址

4.引用

引用出现的目的:指针的书写看起来不好看,代码不美观,使用引用让代码看起来更加清爽美观

啥意思:为对象起了另个一名字(引用即别名)

看个例子:

  1. int int_value = 1024;
  2. //refValue指向int_value,是int_value的另一个名字
  3. int& refValue = int_value;
  4. //注意:引用必须被初始化(引用的底层也是通过指针实现的)

注意:

  • 引用并非对象,只是为一个已经存在的对象起别名
  • 引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起
  • 引用必须初始化,所以使用引用之前不需要测试其有效性,因此使用引用可能会比使用指针效率高
  • 引用不能直接引用另个一常量

引用和指针的关系

  • 引用时对指针进行了简单的封装,底层仍然是指针
  • 获取引用地址时,编译器会进行内部 转换

5.指针与数组

数组:

  • 存储在一块连续的内存空间中
  • 数组名就是这块连续内容空间的首地址
  • 或者说数组名中存放的是数组首个元素的地址
  • 数组数组本质上是:数据类型[ ] 这样一个类型
  1. //指针和数组区别
  2. int main(){
  3. double score[] {11, 22, 33, 44, 55};
  4. double * ptr_score = score;
  5. cout << score <<sizeof(score) << '\t' << sizeof(ptr_score) << endl;
  6. //输出结果:40 4
  7. //数组的大小是40个Byte,一共有5个元素,一个元素8个Byte
  8. //指针的大小是4个Byte,地址的大小就是4Byte(32bit)
  9. }

6.指针运算

6.1 指针的算数运算

  • 指针的递增和递减(++,减减)
  • 一般理解为指针的平移
  • 一个类型为T的指针的移动,以sizeof(T)为移动单位

八、动态分配内存

使用new分配内存

  • 指针的真正用武之地:在运行阶段分配未命名的内存以存储值
  • 在这种情况下,只能通过指针来访访问内存
  • 使用delete释放内存==(类似于c中的free()函数)==

    • 必须与new配对使用
    • 不要释放已经释放的内存
    • 不要释放普通变量的内存
  1. //1.在运行阶段为一个int值分配唯未命名的内存
  2. //2.使用指针(ptr_int)来访问(指向)这个新的内存空间(右->左)
  3. int * ptr_int = new int;
  4. delete ptr_int;//释放由new分配的内存

编译时:(包办婚姻)

int num[56];

运行时:

int * nums = new int[5]

warnning:

1.不要创建两个指向同一块内存的指针,有可能误删除两次

1.动态分配数组

  • 使用 new 创建动态分配的数组

    • int * intArrary = new int[10];
    • new运算符返回第一个元素的地址
  • 使用delete[]释放内存

    • delete [] intArray;
    • 释放整个数组

注意:

  • 不要使用delete释放不是new分配的内存
  • 不要使用delete释放同一个内存两次
  • 如果使用new[]为数组分配内存,则对应delete[]释放内存

2.补充:程序的内存分配

2.1 栈区(stack)

  • 由编译器自动分配释放,一般存放函数的参数值、局部变量等
  • 操作方式类似数据结构中的栈,先进后出
  • 变量名、函数名、指针名等都在栈区

2.2 堆区(heap)

  • 一般由程序员分配释放,若程序不是放,程序结束时可能由操作系统回收
  • 注意:与数据结构中的堆是两回事,分配方式类似链表

2.3 全局区(静态区static)

  • 全局变量和静态变量存储在一起
  • 程序结束后由系统释放

2.4 文字常量区

  • 常量字符串就放在这里,程序结束由系统释放

2.5 程序代码区

  • 存放函数题的二进制代码
  1. int num1 = 0;//全局初始化
  2. int * ptr1; //全局未初始化
  3. int main(){
  4. //栈区
  5. int num2;
  6. //栈区
  7. char str[] = "栋哥";
  8. //栈区
  9. char * ptr;
  10. //“栋哥”以及\0在常量区,ptr3在栈区
  11. char * ptr3 = "栋哥";
  12. //全局(静态)初始化
  13. static int num3 = 1024;
  14. //分配内存在堆区
  15. ptr1 = new int[10];
  16. ptr2 = new char[20];
  17. //注意:ptr1和ptr2本身在栈区中
  18. }

2.6 完成数组的逆序

  1. int arrays[] {15, 23, 30, 40, 5023};
  2. int * ptr_start = arrays;//指向第一个元素
  3. //数组名中存放数组首个元素的地址,所以可以直接赋值给指针变量
  4. int * ptr_end = arrays + 5;//指向最后一个元素
  5. int temp;
  6. //偶数也可以正常运行
  7. while(ptr_star != ptr_end && ptr_end > ptr_star){
  8. temp = *ptr_star;
  9. *ptr_star = *ptr_end;
  10. *ptr_end = temp;
  11. ptr_start++;
  12. ptr_end--;
  13. }
  14. temp = *ptr_star;
  15. *ptr_star = *ptr_end;
  16. *ptr_end = temp;
  17. for(int i = 0; i < 6; i++){
  18. cout << arrays[i] << endl;
  19. }

九、函数

1.函数回顾

1.1 函数分类

  • 内置函数

    • STL标准模板库
    • Boost C++:也是一个C++的库
  • 自定义函数

1.2 函数“三要素”

  • 返回值类型
  • 函数名
  • 参数列表

2 书写自定义函数

  1. int sum(int, int);//函数原形
  2. int main()
  3. {
  4. //函数调用
  5. int result = sum(5, 3);
  6. }
  7. //函数定义
  8. int sum(int num1, int num2)
  9. {
  10. //函数实现的代码
  11. }

一个小例子

  1. using namespace std;
  2. //计算两个数字之和的函数
  3. int sum(int, int); //函数原形
  4. int main()
  5. {
  6. //调用函数
  7. int reuslt = sum(5, 6)
  8. cout << "结果为:" << result << endl;
  9. return 0;
  10. }
  11. //int是返回值类型
  12. int sum(int num1, int num2)
  13. {
  14. //1.计算两个数字之和
  15. int reusult = num1 + num2
  16. //2.返回计算的结果
  17. return result;

注意:

  • 函数原形与函数定义的头部类似,最后以分号结尾
  • 函数原形中的参数名称可以省略,只写参数类型就行

    • int sum(int, double)
  • c++中返回值类型不能是数组,但是可以是其他任何类型(可以将数组作为结构或对象组成部分返回)
  1. /*
  2. 三种形状的体积计算公式如下:
  3. 长方体:长 × 宽 × 高
  4. 圆柱体:圆周率 × 半径的平方 × 高
  5. 圆锥体:1/3 × 底面积 × 高
  6. */
  7. #include <iostream>
  8. #include <iomanip>
  9. #include <windows.h>
  10. #include <cstdlib>
  11. #include <cmath>
  12. using namespace std;
  13. void calcCuboid();//计算长方体的体积
  14. void calcCylinder();//计算圆柱体的体积
  15. int main()
  16. {
  17. //长方体的数据
  18. //cout << "请输入长方体的数据:";
  19. //cin >>"长:">> c1>>"宽">>k1>>"高">>g1;
  20. int choice = -1;
  21. while(choice){
  22. cout << "1、长方体" << endl;
  23. cout << "2、圆柱体" << endl;
  24. cout << "0、退出" <<endl;
  25. cin >> choice;
  26. switch(choice)
  27. {
  28. case 1:
  29. calcCuboid();
  30. break;
  31. case 2:
  32. calcCylinder();
  33. break;
  34. }
  35. }
  36. cout << "感谢您的使用"<<endl;
  37. return 0;
  38. }
  39. void calcCuboid(){
  40. //输入长宽高
  41. double len, width, height;
  42. cout << "请输入长宽高:";
  43. cin >> len >>width >> height;
  44. //计算体积
  45. double v = len * width * height;
  46. cout << "长方体的体积是"<<v<<endl;
  47. }
  48. void calcCylinder(){
  49. //半径和高
  50. double r, height;
  51. cout << "请输入半径和高:";
  52. cin >> r >> height;
  53. //计算体积
  54. double v = 3.14 * pow(r, 2) * height;
  55. cout << "长方体的体积是"<<v<<endl;
  56. }

3 参数和按值传递

  • 按值传递

给函数传递参数(变元)时,参数(变元)值不会直传递给函数,而是先制作参数(变元)的副本,存储在栈上,再使这个副本可用于函数,而不是使用初始值

  1. //这个函数运行的结果仍然是10,即,在main()函数中的变量num的值并没有被改变
  2. //因为第九行num在给change()函数传递参数的时候,仅仅把num中10的值复制了一份传递给了change()函数中的变量num
  3. void change(int num)
  4. {
  5. num++;
  6. }
  7. int main()
  8. {
  9. int num = 10;
  10. change(num);
  11. cout << num << endl;
  12. return 0;
  13. }
  14. //下面这个程序输出的结果是11
  15. /*
  16. 因为change()函数中传递的参数为变量的引用(引用底层由指针实现,其实传递的就是变量的指针)
  17. 所以,main()函数中的变量num的引用被传入到change()函数中以后,其值就会被改变
  18. */
  19. void change(int &num)
  20. {
  21. num++;
  22. }
  23. int main()
  24. {
  25. int num = 10;
  26. change(num);
  27. cout << num << endl;
  28. return 0;
  29. }

4 使用数组作为函数的参数

注意:

  • 数组作为函数实参时,只传递数组的地址==(首地址)==,并不传递整个数组的空间
  • 当用数组名作为实参调用函数时,数组首地址指针就被传递到函数中

5 函数指针

函数也有地址:

  • 函数的地址是存储其机器语言代码的内存开始地址
  • 好处:可以在不同的时间使用不同的函数
  1. //函数指针的声明
  2. //函数原形
  3. double sum(double, double);
  4. //函数指针声明
  5. double (*ptrSum)(double, double)

注意:

  • 该语句声明了一个指针ptrSum,指向一个函数

  • double ptrSum(double, double)
    不是函数指针,而是
    声明了一个函数ptrSum,返回double
    类型

  1. int power(int, int);
  2. int main(){
  3. //根据int power(int, int)声明函数指针
  4. //声明函数指针, 函数的指针名叫ptrPower, 相当于定义普通指针变量时候的int * ptrInt
  5. int (*ptrPower)(int, int);
  6. //声明的函数指针指向函数,以便调用
  7. //因为函数名就是函数的首地址,所以可以直接给指针赋值
  8. ptrPower = power;
  9. cout << ptrpower(3, 4) << endl;
  10. }
  11. int power(int a, int b){
  12. s = power(a, b);
  13. return s;
  14. }

使用函数指针注意事项:

  • 先定义函数(普通函数)
  • 然后声明函数指针
  • 把定义的函数名(原函数的地址)赋值个函数指针

十、函数进阶(C++的特性)

1.内联(inline)函数

  • 是c++为了提高程序运行速度所作的一向改进
  • 与常规函数的区别在于被调用时的运行机制不同

    • 在调用内联函数时,将函数体内的代码,直接赋值一份到调用代码的地方
  • 啥时候使用内联函数?

    • 代码执行时间很短的时候,内联调用就可以节省大部分时间
  1. include <iostream>
  2. //内敛函数可以在函数声明时候加关键字
  3. inline int pow(int, int);
  4. int main()
  5. {
  6. int resutlt = pow(5, 3);
  7. }
  8. //内联函数也可以在定义函数时加关键字inline
  9. inline int pow(int num1, int num2)
  10. {
  11. int result = 1;
  12. for(int i = 0; i < num22; i++){
  13. result *= num1;
  14. }
  15. return result;
  16. }

内联函数的前世今生

  1. #define s(num) num * num
  2. //宏定义了一个S(num)函数
  3. //以后在所有使用S(num)的地方,就自然被替换成num * num

2.引用回顾

引用:为对象起另一个名字(引用即别名)

  • 引用必须进行初始化
  • 引用不能直接引用常量(字面量)

    • 除非,变量也是一个常量
  1. int int_value = 1024;
  2. //refValue指向value,是int_value的另一个名字
  3. int& refValue = int_value;
  4. /**注意:
  5. 1.避免还没有初始化就引用
  6. 2.不能直接引用常量,除非对变量使用const
  7. */
  8. const int & refValue2;

注意:

1.引用不是对象,只是为一个已经存在的对象起的别名

2.引用更紧接const指针,一旦与某个变量关联起来,就将一直效忠于它

3.将引用变量作为参数时,函数将使用原始数据,而非副本**

4.当数据所占内存比较大时,建议使用引用参数

3.使用引用参数

使用引用的理由:

  • 可以更加简便的书写代码
  • 可以直接传递某个对象,而不仅仅只是把对象复制一份
  • 不想让函数修改传入的参数本身时,使用const函数

一个小例子:

  1. #include <iostream>
  2. #include <iomanip>
  3. #include <windows.h>
  4. #include <cstdlib>
  5. #include <cmath>
  6. using namespace std;
  7. //Swap1直接传入参数
  8. void Swap1(int, int);
  9. //Swap2通过指针传入参数
  10. void Swap2(int*, int*);
  11. //Swap3通过引用传入参数
  12. void Swap3(int&, int&);
  13. //show展示函数,不希望在函数里改变变量的值
  14. void show(int&, int&, char);
  15. int main()
  16. {
  17. int num1 = 10, num2 = 5;
  18. Swap1(num1, num2);
  19. show(num1, num2, '1');
  20. Swap2(&num1, &num2);
  21. show(num1, num2, '2');
  22. Swap3(num1, num2);
  23. show(num1, num2, '3');
  24. return 0;
  25. }
  26. void Swap1(int num1, int num2)
  27. {
  28. int temp = num1;
  29. num1 = num2;
  30. num2 = temp;
  31. }
  32. void Swap2(int* num1, int* num2)
  33. {
  34. int temp;
  35. temp = *num1;
  36. *num1 = *num2;
  37. *num2 = temp;
  38. }
  39. void Swap3(int& num1, int& num2)
  40. {
  41. int temp;
  42. temp = num1;
  43. num1 = num2;
  44. num2 = temp;
  45. }
  46. void show(int& num1, int& num2, char name)
  47. {
  48. cout << "执行"<< name <<"交换后num1:" << num1 << "\tnum2:"<< num2 <<endl;
  49. }

使用引用的理由:

  • 可以更加简便地书写代码
  • 可以直接传递某个对象,而不只是把对象赋值一份

4.函数返回引用类型

  • 不要返回局部变量的引用

    • 变量的生存周期
  • ```c int& sum() { int num = 10; //注:rNum是在sum()函数中定义的,所以叫做局部变量 //rNum的生存周期只在sum()函数中! int& rNum = num; return rNum;//返回一个局部变量

    /*函数中的局部变量会被【内存回收】:

    1. 内存回收并不是把内存保存的内容设置为0
    2. 而是指内存中变量申请的这个内存已经分配给了别人。*/

    }

void test(){ int x = 1; int y = 2; int z = 1024; }

int main(){ //result在这里引用了一个局部变量 int& result = sum(); test() cout << “result = “ << result << endl; return 0; }

  1. -
  2. 函数可以不返回值,**默认返回最后一个更新的引用对象(参数)本身**
  3. -
  4. ```c
  5. int& sum(int& num1, int& num2){
  6. num1++;
  7. num2++;
  8. num1++''
  9. //没有返回值,运算结果返回了最后一个跟新的引用
  10. //一定要记得写返回值
  11. return num1 + num2;//肯定报错
  12. }
  13. int main(){
  14. int num1 = 10, num2 = 15;
  15. int& result = sum(num1, num2);
  16. cout << "result = "<< result <<endl;
  17. return 0;
  18. }
  • 返回引用时,要求函数必须包含被返回的引用对象

5.引用参数小结

使用引用参数的一些指导原则

  • 能够修改调用函数中的数据对象
  • 数据对象较大市,传递引用可以提高程序的运行效率

    • 函数中不需要修改传递的参数

      • 如果数据对象很小,就可以按指传递,不用引用
      • 传递数组只能使用指针,并使用const关键字
      • 较大的对象则使用const指针或应用,这样可以避免传递非常大的副本,提高程序运行效率
    • 函数中需要修改传递的参数

      • 数据对象是基本类型或结构时,可以使用指针或者引用(基本类型建议使用指针)
      • 数据对象是数组时只能使用指针
      • 数据对象时类对象时,要求必须使用引用

13.8 聊天小程序

  1. int main(){
  2. }

6.默认参数

  • 使用默认参数

    • ```c void sample(int = 10);//声明的时候把参数的默认值加上 int main(){ sample();//有默认参数不传入参数也不会报错 sample(12); }

//定义的时候就不用写啦 void sample(int num) { cout << num << endl; }

  1. -
  2. 注意:
  3. -
  4. 默认参数可以在函数原型或者定义中给出,不能在这两个位置同时出现
  5. -
  6. 对于带参数列表的函数,必须要求从右向左添加默认值
  7. -
  8. ```c
  9. void test1(int a, int b = 5, int c = 10);//正确
  10. void test2(int a, int b = 5, int c);//错误
  11. void test2(int a = 1, int b = 5, int c = 2);//正确

7.函数重载

函数重载

  • 指可以有多个同名的函数

  • 函数名相同,参数列表不同(特征标不同)

    • 特征标:重载-编译器在编译时,根据参数列表对函数进行重命名

      • ```c void Swap(int a, int b); //编译器编译时:Swap_int_int

void Swap(float a, float b); //编译器编译时:Swap_float_float(重载)

//重载决议

  1. -
  2. 编译器把 引用和类型本身视为同一个特征标
  3. -
  4. 调用匹配函数时,不区分const和非const变量(使不使用const都一样)
  5. <a name="886aa3ac"></a>
  6. #### 使用函数重载实现对不同数据类型的数组进行排序
  7. ```c
  8. int iNums[] = {56, 54, 12, 89, 43};
  9. float fNums[] = {78.0f, 5.7f, 42.8f, 99.1f};
  10. double dNums[] = {78.9, 23.6, 77.8, 98.5, 33.3}

8.函数模板

所谓函数模板,就是建立一个通用函数

  • 函数定义时,不指定具体的数据类型(使用虚拟类型替代)
  • 函数被调用时编译器根据实参反推数据类型-类型的参数化
  1. //函数声明
  2. template<typename T> void Swap(T&, T&);
  3. //typename后面的T是一个虚拟的数据类型
  4. /**
  5. *使用模板技术实现变量交换值
  6. */
  7. template<typename T> //模板头
  8. void Swap(T &a, T &b)
  9. {
  10. T temp = a;
  11. a = b;
  12. b = temp;
  13. }

十一、类与对象

1.面向对象编程

  • 所谓面向对象,就是基于对象概念,以对象为中心,以类和继承为构造机制,来认识、理解、刻画客观世界和设计、构建响应的软件系统(模拟现实)

    • 对象是由==数据(属性)容许的操作(方法)==组成的封装体,与客观世界有直接对应关系
    • 面向对象不是某一种语言的特性,而是一种编程思想
  • 注意:

    • 采用过程性编程方法时,首先考虑要遵循的步骤,然后考虑如何表示这些数据
    • OOP程序员(面向对象)首先会考虑数据,不仅要考虑如何表示这些数据,还要考虑如何使用数据

2.类和对象

2.1 抽象

  • 从具体事物抽取共同本质特征

C++中的类

  • 类是一种将抽象转换为用户定义类型的工具
  • 将数据表示(属性、成员变量)和操纵数据的方法(方法、成员函数)组合成一个整体
  • 类的实例称为对象(类是抽象的,实例是具体的)
  • 类中的变量和函数称为成员

2.2 类的声明

使用class/struct关键字声明类型

  1. class 类名{};
  2. class LandOwner{};
  3. struct 类名{};
  4. struct Hero{};
  • 注意

    • class方式声明的类型与struct声明的类型仅仅是形式上不同

    • 其唯一的区别在于使用class声明的类型默认成员是私有的(private),
      而struct声明的类型默认成员是共有的(public)

3.类的成员变量

  1. //----------------------------.h文件中声明类----------------------------------//
  2. //.h文件中声明类
  3. #ifndef LANDOWNERV2_H
  4. #define LANDOWNERV2_H
  5. #include <iostream>
  6. using namespace std;
  7. class LandOwnerv2
  8. {
  9. public:
  10. string name;
  11. long score;
  12. int cards[20];
  13. public:
  14. LandOwnerv2(); //构造函数的声明
  15. ~LandOwnerv2(); //析构函数的声明
  16. void Touch_card(int); //声明摸牌方法
  17. void show_score(); //声明显示积分方法
  18. /**
  19. * 在类里定义的方法,如果比较短,就直接在类里实现
  20. * 如果方法比较长,则在类里先声明,然后在LandOwner2.cpp文件中定义实现
  21. */
  22. };
  23. #endif // LANDOWNERV2_H
  24. //---------------------/src/xxxxx.cpp文件中实现定义类中方法------------------------------//
  25. //Sources/src/xxxxx.cpp文件中实现定义类中方法
  26. #include "LandOwnerv2.h"
  27. LandOwnerv2::LandOwnerv2()
  28. {
  29. //ctor
  30. }
  31. //实现摸牌方法
  32. //::用来声明Touch_card这个函数是属于LandOwnerv2这个类的
  33. //::作用域解析运算符
  34. void LandOwnerv2::Touch_card(int cardCount)
  35. {
  36. cout << name << "开始摸"<< cardCount <<"张牌" << endl;
  37. }
  38. LandOwnerv2::~LandOwnerv2()
  39. {
  40. //dtor
  41. }
  42. //----------------------------main.cpp文件中创建类实例-----------------------------------//
  43. #include <iostream>
  44. #include "LandOwner_v1.h"//如果要使用类,必须包含类的定义文件
  45. #include "LandOwnerv2.h"
  46. using namespace std;
  47. int main()
  48. {
  49. LandOwner_v1 landOwner1;//定义了一个LandOwner_v1类型变量
  50. //调用对象的成员方法
  51. //landOwner1.cards[0] = 9;//不能直接使用对象的私有成员
  52. landOwner1.TouchCard(100);
  53. cout << "Hello world!" << endl;
  54. LandOwnerv2 LandOwner2;
  55. LandOwner2.name = "栋哥";
  56. LandOwner2.Touch_card(20);
  57. return 0;
  58. }

4.访问修饰符

常见访问修饰符

  • public:修饰的成员在任意地方都可以访问
  • private:修饰的成员只能够在类中或者友元函数中可以访问
  • protected:修饰的成员可以在类中函数,子类函数及友元函数中访问

修饰成员

  • 将修饰关键字放置在类定义的大括号中,添加冒号

  • ```c class 类名(){ 修饰符:

    1. 成员列表

    };

class LandOwner{ private: string name; public: void PlayCard(); };

  1. -
  2. 如果不写修饰符则默认为private
  3. ```c
  4. class LandOwnerv4
  5. {
  6. private://如果省略了private也代表私有类
  7. long score;
  8. string name;
  9. int cards[20];
  10. public:
  11. LandOwnerv4();
  12. virtual ~LandOwnerv4();
  13. long Getscore() { return score; }
  14. void Setscore(long val) {
  15. if(val < 0){
  16. score = 0;
  17. //传入的分数有问题
  18. }
  19. else{
  20. score = val;
  21. }
  22. }//使用工具自动生成Get/Set
  23. string Getname() { return name; }
  24. void Setname(string val) { name = val; }
  25. //int Getcards[20]() { return cards[20]; }
  26. //void Setcards[20](int val) { cards[20] = val; }
  27. protected:
  28. };
  29. #endif // LANDOWNERV4_H

5.构造函数和析构函数

构造函数

  • 以类名作为函数名
  • 无返回值类型

作用

  • 初始化对象的数据成员
  • 类对象被创建时,编译器为对象分配内存空间,并自动调用构造函数以完成成员初始化
  • 对成员变量进行初始化

构造函数的种类:

  • 无参构造
  • 一般构造(重载构造)
  • 拷贝构造

注意:

  • 如果创建的类中没有书写任何构造函数,系统会自动生成默认的无参构造函数(函数为空,什么都不做)
  • 如果书写了构造函数,系统就不会再自动生成默认构造,如果希望有一个这样的无参构造函数,需要自己显示地写出来
  1. /**-------------------------LandOwnerv4.cpp--------------------------------
  2. 定义构造函数,设置对象的初始值*/
  3. LandOwnerv4::LandOwnerv4()
  4. {
  5. //ctor
  6. //一般使用构造函数进行成员变量的初始化
  7. name = "默认地主";
  8. score = 0;
  9. //将用户的手牌数组初始化为0
  10. memset(cards, 0, sizeof(cards));
  11. /**
  12. memset()函数,把数组cards的所有元素的值都设置为0,一共要设置cards个元素
  13. 参数1:要初始化的数组
  14. 参数2:每个元素要设置的值
  15. 参数3:数组的大小
  16. */
  17. cout << "LandOwern4的无参构造函数(默认构造)被调用"<< endl;
  18. cout << "初始化结果如下:" << endl;
  19. cout << "名称:" << name << endl;
  20. cout << "积分:" << score << endl;
  21. cout << "手牌数组:";
  22. for(int i = 0; i < sizeof(cards) / sizeof(cards[0]); i++){
  23. cout << cards[i] << ".";
  24. }
  25. cout << endl;
  26. }
  27. //---------------------------LandOwnerv4.h----------------------------------//
  28. #ifndef LANDOWNERV4_H
  29. #define LANDOWNERV4_H
  30. #include <iostream>
  31. #include <memory.h>
  32. using namespace std;
  33. //使用工具自动生成get/set方法
  34. class LandOwnerv4
  35. {
  36. public:
  37. LandOwnerv4();//41行就是构造函数,默认构造写不写都是存在的
  38. virtual ~LandOwnerv4();
  39. long Getscore() { return score; }
  40. void Setscore(long val) {
  41. if(val < 0){
  42. score = 0;
  43. //传入的分数有问题
  44. }
  45. else{
  46. score = val;
  47. }
  48. }
  49. string Getname() { return name; }
  50. void Setname(string val) { name = val; }
  51. //int Getcards[20]() { return cards[20]; }
  52. //void Setcards[20](int val) { cards[20] = val; }
  53. protected:
  54. private:
  55. long score;
  56. string name;
  57. int cards[20];
  58. };
  59. #endif // LANDOWNERV4_H
  60. //---------------------------main.cpp----------------------------------//
  61. int main{
  62. LandOwner4 LandOwner01();//默认构造
  63. LandOwner4 LandOwner02;//默认构造的省略写法
  64. }

5.1 带参构造

  1. //------------------Student.h-----------------------------//
  2. #ifndef STUDENT_H
  3. #define STUDENT_H
  4. #include <iostream>
  5. using namespace std;
  6. class Student
  7. {
  8. public:
  9. //构造函数的重载规则与普通函数相同
  10. Student();//这个一个默认构造
  11. Student(string name, string dasc);//这是一个带参构造
  12. Student(int age);//这是一个只有一个参数的带参构造
  13. //如果构造函数中只有一个是唯一的参数,
  14. //Student stu4 = 40;可以这样直接定义
  15. ~Student();
  16. void ShowInfo();//这个一个展示任务信息的函数
  17. string Getname() { return m_name; }
  18. void Setname(string val) { m_name = val; }
  19. string Getdesc() { return m_desc; }
  20. void Setdesc(string val) { m_desc = val; }
  21. int Getage() { return m_age; }
  22. void Setage(int val) {
  23. if(val < 0){
  24. m_age = 18;
  25. }else
  26. m_age = val;
  27. }
  28. protected:
  29. private:
  30. string m_name;
  31. string m_desc;
  32. int m_age;
  33. };
  34. #endif // STUDENT_H
  35. //--------------------------Student.cpp---------------------------//
  36. #include "Student.h"
  37. Student::Student()
  38. {
  39. //ctor
  40. cout << "这是一个默认构造"<<endl;
  41. }
  42. Student::Student(int age)
  43. {
  44. Setage(age);
  45. cout << "调用带参构造:Student(int age)"<<endl;
  46. }
  47. Student::Student(string name, string desc)
  48. {
  49. m_name = name;//等价写法:SetName(name);
  50. m_desc = desc;
  51. cout << "调用带参构造:Student(string name, string dasc)"<< endl;
  52. }
  53. void Student::ShowInfo()
  54. {
  55. cout << m_desc << m_name << endl;
  56. }
  57. Student::~Student()
  58. {
  59. //dtor
  60. }
  61. //-------------------------main.cpp------------------------//
  62. #include <iostream>
  63. #include "LandOwner_v1.h"//如果要使用类,必须包含类的定义文件
  64. #include "LandOwnerv2.h"
  65. #include "LandOwnerv3.h"
  66. #include "LandOwnerv4.h"
  67. #include "Student.h"
  68. using namespace std;
  69. int main()
  70. { //LandOwnerv4 andOwnerv4;
  71. Student stu1;//在栈内存(速度快)中直接分配空间
  72. Student stu2("马化腾","普通家庭");
  73. Student stu3(40);
  74. cout << "马化腾的年龄是:"<< stu3.Getage() <<endl;
  75. stu2.ShowInfo();
  76. Student* stu5 = new Student("杰克马","悔创阿里");
  77. //在堆内存中分配空间,指针指向堆内存,分配内存的同时,调用构造函数
  78. stu5->ShowInfo();//指针访问属性或者方法:->
  79. return 0;
  80. }

5.2 栈内存和堆内存

栈内的数据可以共享,存取速度非常快,但是栈内存里不能放太多的东西,相当于零售店,看上啥可以直接买走

堆内存相当于大仓库,可以存放更大的数据,速度相对堆内存来说慢一些

5.3 析构函数

析构函数:对象出生的收调用构造函数,结束的时候调用析构函数

  • 对象过期时自动调用的特殊成员函数
  • 析构函数一般用来完成清理工作
  • 析构函数的名称是在类名前加上==~==

    • 析构函数没有参数,只能有一个

注意:

  • 析构函数用来释放对象使用的资源,并销毁对象的非static数据成员
  • 无论如何时一个对象销毁,都会自动调用其析构函数(隐式析构)
  • 栈空间内的变量自动被释放,堆内存不会被自动释放

    • 所以当对象使用完毕时要delete释放内存

6.this指针

  • 每个成员函数(包括构造和析构)都有一个this指针
  • this指针指向调用的对象,即可以通过this关键字访问当前对象的成员

    • 访问成员变量

      • this->变量名
    • 访问成员函数

      • this->函数名