《面向对象程序设计》

课程大作业

目  录

( 2021 / 2022学年 第二学期). 1

第一章  绪论. 1

1.1面向对象程序特点与目标. 1

1.2面向对象程序知识架构. 1

第二章  面向对象程序设计关键. 2

2.1引用与重载. 2

2.1.1引用做函数参数. 2

2.1.2引用做函数返回值. 3

2.1.3引用的本质. 4

2.1.4常量引用. 5

2.1.5函数重载. 6

2.2封装. 8

2.2.1访问权限. 8

2.2.2成员属性私有化. 9

2.3对象特性. 11

2.3.1构造函数. 11

2.3.2析构函数. 12

2.3.3拷贝构造函数. 13

2.3.4静态成员. 16

2.4this指针. 18

2.5友元. 22

2.5.1全局函数做友元. 22

2.5.2友元类. 23

2.5.3成员函数做友元. 25

2.6继承. 27

2.6.1继承方式. 31

2.6.2同名成员处理. 35

2.6.3菱形继承. 37

2.7多态. 38

2.7.1多态基本语法. 38

2.7.2纯虚函数与抽象类. 40

2.7.3虚析构与纯虚析构. 41

2.8文件操作. 46

2.8.1文本文件. 46

2.8.2二进制文件. 49

第三章面向对象应用. 51

3.1矩阵类设计及应用. 51

3.2银行账号管理系统设计及应用. 55

结束语. 72

第一章  绪论

1.1面向对象程序特点与目标

  面向对象程序设计的主要特点为:抽象,封装,继承,多态。①抽象,也就是将具体的实例化特点抽样出共同的属性与功能,例如人类有共有的属性,比如:年龄,姓名,性别等。但是每个人却又彼此不同,每个人具有自己的属性与能力,每一个人都是整个人类的某一个对象②封装,将有关数据与操作封存在对象之中,形成一个独立的个体,避免与其他个体产生冲突,互相干扰。并且将一部分属性进行私有化,保障数据信息安全③继承,通过类似于父子的继承方式,精简代码,减少相应属性操作代码重复冗杂④多态,在继承的前提下,产生更多不同的其他派别,不同对象对于同一消息会产生不同的响应。

1.2面向对象程序知识架构

  以对象为核心,通过设置对象的属性,并对其进行封装,改变对象的权限范围以及应用范围,解决现实的问题。通过多个类,使单个能够起到作用的子对象组合成符合多功能的统一对象组合,通过继承使单一子对象具有向下发展向外拓展的可持续发展可能性,多态则保证了向外拓展的多样性,也同时保证在程序运用使的普适性广泛性

第二章  面向对象程序设计关键

2.1引用与重载

2.1.1引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参

优点:简化指针修改实参

函数参数主要有值传递,地址传递以及引用三种方式,值传递中形式参数的改变不会对实际参数产生影响,地址传递中通过取地址与指针的方式,实现了形式参数对实际参数的改变。而由于指针传递的方式较为难以理解,并且指针传递会开辟一块新的内存空间,所以我们采用引用的方式实现对实际参数的改变。

代码实现;

  1. #include<iostream>  
  2. using namespace std;  
  3. //值传递,形式参数改变,实际参数不改变
  4. void trans01(int a,int b)
  5. {
  6. int temp = a;
  7. a = b;
  8. b = temp;
  9. }
  10. //地址传递,实际参数改变
  11. void trans02(int *a,int *b)
  12. {
  13. int temp = *a;
  14. *a = *b;
  15. *b = temp;
  16. }
  17. //引用,相当于取别名,a就是m,b就是n
  18. void trans03(int &a,int &b)
  19. {
  20. int temp = a;
  21. a = b;
  22. b = temp;
  23. }
  24. int main()
  25. {
  26. int m, n;
  27. m = 1;
  28. n = 2;
  29. trans01(m,n);
  30. trans02(&m,&n);
  31. trans03(m,n);
  32. system("pause");
  33. return 0;
  34. }

可以明显的看出,引用实现实参改变的方法,函数主体与值传递相同,所以我们可以将引用理解为取别名,也就是给实参取一个小名,小名变化大名也变化。简化了函数形式参数的运用,提升了代码的易读程度,降低了编写难度。

2.1.2引用做函数返回值

作用:引用是可以作为函数的返回值存在的

注意:不要返回局部变量引用

用法:函数调用作为左值

用引用作为函数的返回值的最大好处是在内存中不产生返回值的副本,直接将变量返回给函数,避免临时变量的产生。当变量属于类的对象时,这样还可以避免调用拷贝构造函数在内存中创建临时对象的过程,提高了程序的时间和空间使用效率

代码实现:

  1. #include<iostream>  
  2. using namespace std;  
  3. //局部变量不能作为函数的返回值
  4. int&  test01()
  5. {
  6. int a = 10;
  7. //作为局部变量,储存在栈区,函数运行结束后即释放
  8. return a;//返回局部变量引用
  9. }
  10. //引用函数可以作为左值
  11. int& test02()
  12. {
  13. static int a = 20;
  14. //作为静态局部变量,储存在全局区,在程序运行结束后自动释放
  15. return a;//返回静态变量引用
  16. }
  17. int main()
  18. {
  19. int &ret=test01();
  20. cout << ret << endl;//10
  21. cout << ret << endl;//2033293704
  22. //函数值第一次打印正确,第二次打印为乱码,是由于编辑器对返回值自动做了一次保存
  23. //而第二次打印的时候就不会再次保存了
  24. int& ret2 = test02();
  25. cout << ret2 << endl;//20
  26. cout << ret2 << endl;//20
  27. test02() = 2000;
  28. //如果函数作为左值,则必须返回引用
  29. //函数可以作为左值,因为函数返回值与ret2是同一事物,即a与ret2为同一主体的不同名字
  30. cout << ret2 << endl;//2000
  31. cout << ret2 << endl;//2000
  32. ret2 = 100;
  33. cout << ret2 << endl;//100
  34. cout << test02() << endl;//100
  35. system("pause");
  36. return 0;
  37. }

2.1.3引用的本质

引用的本质就是一个指针常量,引用一旦初始化之后,就不可以发生改变。

  表面上:Int &ref=a;

实际上:int * const ref =&a;

代码实现:

  1. #include<iostream>  
  2. using namespace std;  
  3. void func(int &ref)
  4. {
  5. ref = 100;//ref为引用,转换为*ref=100
  6. }
  7. int main()
  8. {
  9. int a = 10000;
  10. int& ref = a;
  11. //编辑器发现引用,转换为int * const ref=&a;
  12. //由于ref为有关键字const修饰,所以为固定对象不可修改
  13. ref = 20;//*ref=20;
  14. cout << "a=" << a << endl;//20
  15. cout << "ref=" << ref << endl;//20
  16. func(a);
  17. cout << ref << endl;//100
  18. system("pause");
  19. return 0;
  20. }

2.1.4常量引用

引用无法指向一个常量,必须指向一个栈区或者堆区的可变数据。引用本省需要一个合法的内存空间,因此例如:int &a=10;这样的代码是错误的  

而Const int &a=10;是正确的

编译器会将其自动转换为

  1. int temp=10;  
  2. Const int &a=temp;

函数中常常利用常量引用防止误操作修改实参,引用的使用常见,通常是用来就是形式参数

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. void test01(int &ref)
  4. {
  5. ref = 1000;
  6. cout << "ref="<<ref << endl;
  7. }
  8. void test02(const int& ref2)
  9. //实现输出,避免变量改变导致打印值变化
  10. {
  11. //ref2 = 10;//这里会报错,因为ref有const修饰,不可修改
  12. cout << "ref2=" << ref2 << endl;
  13. }
  14. int main()
  15. {
  16. int a = 10;
  17. test01(a);//1000
  18. test02(a);//1000
  19. system("pause");
  20. return 0;
  21. }

2.1.5函数重载

     函数默认参数:void func(int a,int b=10){}

函数的默认参数即是给形式参数一个初值,如果函数体中没有给该形式参数赋值,则使用函数定义中的默认参数,有赋值则采用赋值值。①如果某个位置参数有默认值,那么从这个位置往后,从左向右都必须有默认值②如果函数的声明有默认值,那么函数实现的时候就不能有默认参数。

函数占位参数:void func(int a,int){}

函数重载满足条件:①在同一个作用域下②函数名称相同③函数参数类型不同或者个数不同或者顺序不同

注意:函数的返回值不可以作为函数重载的条件

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. void func()
  4. {
  5. cout << "func的调用" << endl;
  6. }
  7. void func(int a)
  8. {
  9. cout << "func(int a)的调用" << endl;
  10. }
  11. void func(double a)
  12. {
  13. cout << "func(double a)的调用" << endl;
  14. }
  15. void func(int a, int b)
  16. {
  17. cout << "func(int a,int b)的调用" << endl;
  18. }
  19. void func(double a, int b)
  20. {
  21. cout << "func(double a,int b)的调用" << endl;
  22. }
  23. int main()
  24. {
  25. func();
  26. func(1);
  27. func(1.0);
  28. func(1, 1);
  29. func(1.0, 1);
  30. system("pause");
  31. return 0;
  32. }

函数重载的注意事项:

1)引用作为函数重载的条件

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. //引用作为函数参数
  4. void fun(int& a)
  5. {
  6. cout << "fun(int& a)的调用" << endl;
  7. }
  8. void fun(const int& a)
  9. {
  10. cout << "fun(const int& a)的调用" << endl;
  11. }
  12. int main()
  13. {
  14. int a = 10;
  15. fun(a);
  16. fun(10);
  17. system("pause");
  18. return 0;
  19. }

2)函数重载碰到函数默认参数

要尽量减少函数重载与默认参数同时出现,避免出现同一个函数调用适用于多个函数的情况,避免产生生成错误,出现二义性导致崩溃

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. //函数重载碰到默认参数
  4. void func(int a)
  5. {
  6. cout << "func(int a)的调用" << endl;
  7. }
  8. void func(int a, int b = 10)
  9. {
  10. cout << "func(int a,int b=10)的调用" << endl;
  11. }
  12. int main()
  13. {
  14. int a = 10;
  15. //func(a);
  16. //同时满足上面两个函数,编译器无法判断,出现二义性
  17. system("pause");
  18. return 0;
  19. }

2.2封装

2.2.1访问权限

将类内分为公共权限(public)、保护权限(protected)、私有权限(private)

公共权限:类内可以访问,类外也可以访问

保护权限:类内可以访问,类外不能访问

私有权限:类内可以访问,类外不能访问

代码实现:

  1. #include<iostream>  
  2. using namespace std;  
  3. class human
  4. {
  5. public://公共权限
  6. string h_name;
  7. protected://保护权限
  8. string h_car;
  9. private:// 私有权限
  10. int h_password;
  11. public:
  12. void func()
  13. {
  14. h_name = "张三";
  15. h_car = "拖拉机";
  16. h_password = "12345";
  17. }
  18. };
  19. int main()
  20. {
  21. human s1;
  22. s1.h_name = "李四";
  23. //s1.h_car = "奔驰";//显示不可访问,因为car属于保护权限内容,在类外访问不到
  24. //s1.h_passworld = "123";//同样显示不可访问,私有权限类外不可访问
  25. system("pause");
  26. return 0;
  27. }

注意:class与struct的区别

Class中的默认权限是私有权限,而struct中的默认权限是公共权限

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. //class默认权限是私有权限private
  4. //struct默认权限是公共权限public
  5. class f1
  6. {
  7. int m_a;//私有
  8. };
  9. struct f2
  10. {
  11. int m_a;//公共
  12. };
  13. int main()
  14. {
  15. f1 f1;
  16. //f.m_a = 100;//报错不可访问,因为class默认为私有权限
  17. f2 f2;
  18. f2.m_a = 100;//默认权限为公共,所以可以访问
  19. system("pause");
  20. return 0;
  21. }

2.2.2成员属性私有化

优点:①将所有成员属性设置为私有,可以自己控制读写权力②对于写权限,我们可以检测数据的有效性,可以在读写中设置if等选择语句进行判别

代码实现:

  1. #include<iostream>  
  2. #include<string>  
  3. using namespace std;  
  4. class person
  5. {
  6. public://将成员属性私有化,需要在公共权限提供接口
  7. //写姓名
  8. void setname(string name)
  9. {
  10. m_name = name;
  11. }
  12. //读姓名
  13. string getname()
  14. {
  15. return m_name;
  16. }
  17. int getage()
  18. {
  19. int m_age = 16;
  20. return m_age;
  21. }
  22. void setlover(string lover)
  23. {
  24. m_lover = lover;
  25. }
  26. //输入财产
  27. void setmoney(int money)
  28. {
  29. m_money = money;
  30. }
  31. //输出财产
  32. int getmoney()
  33. {
  34. if (m_money < 0 ||m_money>1000)
  35. {
  36. cout << "财产不正常" << endl;
  37. return 0;
  38. }
  39. return m_money;
  40. }
  41. private:
  42. string m_name;//姓名   可读可写
  43. int m_age;//年龄       只读
  44. string m_lover;//爱人  只写
  45. int m_money;//财产  可读可写
  46. };
  47. int main()
  48. {
  49. person d1;
  50. d1.setname("张三");
  51. //d1.m_name = "张三";    不可以使用这条语句,因为m_name属于私有权限
  52. cout << "姓名:" << d1.getname() << endl;
  53. //d1.getage = 18;
  54. //d1.getage("18");
  55. //这两种都是不可以的,因为getage没有形参也就没有输入,不可以赋值
  56. cout << "年龄:" << d1.getage() << endl;
  57. d1.setlover("xx");
  58. //cout << "爱人" << d1.setlover << endl;//报错因为函数无返回值
  59. d1.setmoney(100);
  60. cout << "财富:" << d1.getmoney() << endl;
  61. system("pause");
  62. return 0;
  63. }

2.3对象特性

2.3.1构造函数

构造函数:主要作用在创建对象时,为对象赋初值

对象的初始化与清除也是十分重要的问题,在对象的开始默认使用构造函数,完成对对象的初始化工作,如果不主动提供构造函数,则编译器会自动强制使用编译器自带的构造函数,为空实现

注意:①构造函数没有返回值,也不写void②函数名称与类名相同③构造函数可以有参数,因此也可以函数重载④程序在调用时会自动调用构造函数,无需手动调用,且程序允许一次只会调用一次

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. class Person
  4. {
  5. public:
  6. Person()
  7. {
  8. cout << "Person构造函数的调用" << endl;
  9. }
  10. }
  11. void test01()
  12. {
  13. Person P;//在栈区上的数据,函数执行完毕后内存空间释放
  14. }
  15. int main()
  16. {
  17. test01();
  18. system("pause");
  19. return 0;//main函数运行完毕后也会执行析构函数,只是由于对话框关闭显示时间极短
  20. }
  21. */

构造函数分为无参构造函数(默认构造函数)和有参构造函数

1)无参:Person()

调用无参构造:person p1

调用默认构造时不加(),否则编译器会认为是一个函数的声明

2)有参:person(int a)

调用有参构造:person p2(10)

2.3.2析构函数

析构函数:主要作用在销毁对象是,执行清理

对象的初始化与清除也是十分重要的问题,在对象的结束默认使用析构函数,完成对对象的清理工作,如果不主动提供析构函数,则编译器会自动强制使用编译器自带的析构函数,为空实现

注意:①析构函数没有返回值,也不写void②函数名称与类名相同,并在名称前面加~③析构函数不可以有参数,无法发生重载④程序在对象销毁之前会自动调用析构函数,无需手动调用,且程序结束一次只会调用一次

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. class Person
  4. {
  5. public:
  6. ~Person()
  7. {
  8. cout << "~Person析构函数的调用" << endl;
  9. }
  10. };
  11. void test01()
  12. {
  13. Person P;//在栈区上的数据,函数执行完毕后内存空间释放
  14. }
  15. int main()
  16. {
  17. test01();
  18. system("pause");
  19. return 0;//main函数运行完毕后也会执行析构函数,只是由于对话框关闭显示时间极短
  20. }

2.3.3拷贝构造函数

  用法:①使用一个已经创建完毕的对象来初始化一个新的对象②以值传递的方式给函数参数传值③以值方式返回局部对象

  编辑器默认的拷贝构造函数是一个浅拷贝,直接进行值传递拷贝

  代码实现:

  1. #include<iostream>  
  2. using namespace std;  
  3. class person
  4. {
  5. public:
  6. person()
  7. {
  8. cout << "person默认构造函数的调用" << endl;
  9. }
  10. ~person()
  11. {
  12. cout << "person析构函数的调用" << endl;
  13. }
  14. person(int age)
  15. {
  16. cout << "有参构造函数的调用" << endl;
  17. m_age = age;
  18. }
  19. person(const person& p)
  20. {
  21. cout << "拷贝构造函数的调用" << endl;
  22. m_age = p.m_age;
  23. }
  24. int m_age;
  25. };
  26. //1.使用一个已经创建完毕的对象来初始化一个新对象
  27. void test01()
  28. {
  29. person p1(20);//有参构造函数
  30. person p2(p1);//拷贝构造函数
  31. cout << "p2年龄为" << p2.m_age << endl;
  32. }
  33. //2.值传递的方式给函数参数传值
  34. void dowork(person p)
  35. {
  36. }
  37. void test02()
  38. {
  39. person p;//默认构造函数调用
  40. dowork(p);//实参传给形参时使用了一个拷贝构造函数,为临时副本
  41. }
  42. //3.值方式返回局部对象
  43. person dowork1()
  44. {
  45. person p1;
  46. return p1;
  47. //由于创建临时对象,此时p1已经消失,return的是p1的克隆体,person p接收的也是克隆体的数据
  48. }
  49. void test03()
  50. {
  51. person p = dowork1();//相当于person p=person(p1)
  52. }
  53. int main()
  54. {
  55. //test01();
  56. //test02();
  57. test03();
  58. system("pause");
  59. return 0;
  60. }

运行结果:

深拷贝与浅拷贝

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

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

当我们对p2.m_height进行析构函数释放空间时,堆区内存以及被释放掉了,此时p1再次执行析构函数释放堆区内存,会导致同一块内存区域被连续释放两次,为非法操作。因此我们引入深拷贝,使p1,p2在堆区分别开辟一块空间,这样数据释放时就不会重复

编译器提供的浅拷贝(默认实现):

  1. person(const person& p)
  2. {
  3. cout << "person拷贝构造函数调用" << endl;
  4. m_age = p.m_age;
  5. m_height=p.m_height
  6. }

自己实现深拷贝:

  1. person(const person& p)
  2. {
  3. cout << "person拷贝构造函数调用" << endl;
  4. m_age = p.m_age;
  5. m_height = new int(*p.m_height);
  6. }

代码实现:

  1. //深拷贝
  2. //深拷贝与浅拷贝的区别在于浅拷贝直接进行值复制传递,两次传递共用一个地址,所以在析构函数的释放过程中会重复释放堆区内存
  3. //浅拷贝地址相同而深拷贝地址不同
  4. #include<iostream>
  5. using namespace std;
  6. class person
  7. {
  8. public:
  9. person(int age, int hight)
  10. {
  11. m_age = age;//全局区中的内存数据
  12. m_hight = new int(hight);//在堆区开辟一个数据hight,并将这个hight的值赋值给m_hight,这里的m_hight为指针变量
  13. cout << "person有参构造函数调用" << endl;
  14. }
  15. ~person()
  16. {
  17. //析构代码通常将堆区开辟的数据进行释放
  18. if (m_hight != NULL)
  19. {
  20. delete m_hight;
  21. m_hight = NULL;//防止野指针出现
  22. }
  23. cout << "person析构函数调用" << endl;
  24. }
  25. person(const person& p)//构造函数重载,通过形式参数的改变,重新构造拷贝函数,其中的参数为地址且为常量
  26. {
  27. cout << "拷贝函数调用" << endl;
  28. m_age = p.m_age;
  29. m_hight = new int(*p.m_hight);//深拷贝,开辟一个堆区空间用于存放m_hight的地址,并用m_hight来接收
  30. //m_hight=p.m_hight,这里是浅拷贝方法,直接值拷贝
  31. }
  32. int m_age;
  33. int* m_hight;//由于接收的是堆区中存放的地址,所以为整型指针变量
  34. };
  35. void test01()
  36. {
  37. person p1(18, 170);
  38. cout << "p1年龄为" << p1.m_age << "\t" << "p1身高为" << *p1.m_hight << endl;
  39. person p2(p1);
  40. cout << "p2年龄为" << p2.m_age << "\t" << "p2身高为" << *p2.m_hight << endl;
  41. }
  42. int main()
  43. {
  44. test01();
  45. system("pause");
  46. return 0;
  47. }

2.3.4静态成员

静态成员变量:①所有对象共享一份数据②在编译阶段分配内存③类内声明类外初始化

静态成员函数:①所有对象共享同一个函数②静态成员函数只能访问静态成员变量

非静态成员变量必须要对象特定的类,假如同时有p1,p2,则无法确定非静态成员变量m_b是p1还是p2的

通过对象访问静态成员函数

通过类名访问静态成员函数

Person p;

P.func();

Person::func()

代码实现:

  1. //类内的成员变量和成员函数分开储存  
  2. //只有非静态成员变量才属于类的对象上  
  3. #include<iostream>  
  4. using namespace std;  
  5. class person
  6. {
  7. public:
  8. int m_A;//非静态成员变量,属于类的对象上
  9. static int m_B;//静态成员变量,不属于类的对象上,不增加类的内存空间
  10. void func1()//非静态成员函数,不属于类的对象上,不增加类的内存空间
  11. {
  12. }
  13. static void func2()//静态成员函数,不属于类的对象上,不增加类的内存空间
  14. {
  15. }
  16. };
  17. int person::m_B = 200;
  18. void test01()
  19. {
  20. person p1;
  21. //空对象占用内存空间为 1
  22. //c++编译器会给每个空对象也分配一个字节空间
  23. //用于区分空对象占内存的位置,每个空对象也会有一个独一无二的内存地址
  24. cout <<" sizeof p1 =" <<sizeof(p1)<< endl;
  25. }
  26. void test02()
  27. {
  28. person p2;
  29. cout << "sizeof p2 =" << sizeof(p2) << endl;//4
  30. }
  31. class ani
  32. {
  33. public:
  34. double m_M;
  35. };
  36. void test03()
  37. {
  38. ani a1;
  39. cout << "sizeof a1 =" << sizeof(a1) << endl;//8,因为m_M为双精度型
  40. }
  41. int main()
  42. {
  43. test01();
  44. test02();
  45. test03();
  46. system("pause");
  47. return 0;
  48. }

静态成员函数同样具有访问权限,private私有作用域下类外无法访问

2.4this指针

每一个非静态成员函数只会产生一份函数实例,也就是说多个同类型的对象会使用同一块代码。为了区分对象,引入this指针,指向被调用的成员函数所属的对象

P1

This指向p1

P2

This指向p2

P3

This指向p3

用途:①当形参与成员变量同名时,使用this指针来区分,this指向的为属性中定义的,未被指向的为形式参数

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

代码实现:

  1. #include<iostream>   
  2. using namespace std;  
  3. class person
  4. {
  5. public:
  6. person(int age)
  7. {
  8. age = age;
  9. }
  10. int age;
  11. };
  12. void test01()
  13. {
  14. person p1(18);
  15. cout << "p1的年龄为:" << p1.age << endl;
  16. //程序运行出错,显示乱码,形式参数与属性同名
  17. }
  18. class high
  19. {
  20. public:
  21. high(int tall)
  22. {
  23. //this指针指向被调用的成员函数所属的对象
  24. this->tall = tall;
  25. }
  26. high& addhigh01(high &h)
  27. //当函数的返回值为void时,此函数不能被连续调用
  28. //例如:h3.addhigh(h2).addhigh(h1)是错误的
  29. {
  30. this->tall += h.tall;
  31. return *this;//用*this做函数的返回值
  32. }
  33. high addhigh02(high &h)
  34. {
  35. this->tall += h.tall;
  36. return *this;
  37. }
  38. int tall;
  39. };
  40. void test02()
  41. {
  42. high h1(170);//h1调用有参构造函数,所以this指向h1
  43. high h2(200);
  44. cout << "h1的高度为:" << h1.tall << endl;//170
  45. cout << "h2的高度为:" << h2.tall << endl;//200
  46. }
  47. void test03()
  48. {
  49. high h3(120);
  50. high h4(150);
  51. h4.addhigh01(h3);
  52. cout << "h4的身高加上h3的身高后为:" << h4.tall << endl;
  53. high h5(200);
  54. h5.addhigh01(h4).addhigh01(h3);
  55. //可以无限向后追加,是链式编程思想
  56. cout << "采用addhigh01,返回值为high&的函数" << endl;
  57. cout << "h5+(h4+h3)+h3后身高为:" << h5.tall << endl;
  58. }
  59. void test04()
  60. {
  61. high m1(50);
  62. high m2(100);
  63. m1.addhigh02(m2).addhigh02(m2);
  64. cout << "m1.addhigh02(m2).addhigh02(m2);后m1身高为:" << m1.tall << endl;
  65. //150,m1=50+100,后面的一个100加到m1'上使得m1’=150+100,m1’为m1的拷贝复制所得
  66. //addhigh02的函数返回值是high,是值传递,为拷贝构造函数,每调用一次都会创造一个新的对象
  67. high m3(50);
  68. high m4(100);
  69. m3.addhigh01(m4).addhigh01(m4);
  70. cout << "m3.addhigh01(m4).addhigh01(m4)后m3身高为:" << m3.tall << endl;
  71. //250,m3=50+100+100,从始至终都是一个对象m3
  72. //addhigh01的函数返回值是high&,是引用的方式返回,不会创建新的对象
  73. }
  74. int main()
  75. {
  76. test01();//显示为乱码,因为三个age编辑器认为是同一个
  77. test02();
  78. test03();
  79. test04();
  80. system("pause");
  81. return 0;
  82. }

This为类自带指针,所以返回值类型为类名,返回对象本身用*this

返回值类型

Void

High

High&

只可调用一次,由于无返回值,所以不能连续调用

可以无限的向后叠加运算,但是返回值类型为值传递,每次向后运算都会增加一个新的对象

用引用的方式返回,返回为本体,之后不论进行多少次调用运算,仍然是对同一个进行运行,也就是对初始的类名运算

X1=1+1

X2=x1+1=2+1

X3=x2+1=3+1

X=1+1+1+1+…

空指针:

空指针:类型为void的指针,void*可以指向任意类型的对象

说明指针与地址值有关,只是不知道储存在这个地址上的对象的类型

支持:①与另一指针比较②向函数传递void*指针③函数返回void*指针④给另一个void*指针赋值

不支持:①解引用,无法获得指向对象的值②不能进行移位操作等指针运算

  1. //空指针也可以调用成员函数
  2. #include<iostream>
  3. using namespace std;
  4. //补充
  5. //野指针:指向一个非法的或以及销毁的内存指针,指针变量没有被初始化,不知道指向地址为什么
  6. //如果指针被free释放或者delete删除后,指针所指的内存被释放,没有改变指针的值,指针就落为野指针
  7. //可以理解为亲戚搬家后还留着旧地址
  8. class person
  9. {
  10. public:
  11. person(int age)
  12. {
  13. m_age = age;
  14. }
  15. void showclassname()
  16. {
  17. cout << "this is person class" << endl;
  18. }
  19. void showpersonage()
  20. {
  21. cout << "age=" << this->m_age << endl;//this是类内默认的隐含操作
  22. }
  23. int m_age;
  24. };
  25. void test01()
  26. {
  27. person* p = NULL;//p是一个空指针,与野指针不同
  28. //p->m_age = 18;
  29. //代码崩溃,p是一个空指针,无法对p内的m_age赋值,因为指针内部为空,无属性
  30. p->showclassname();
  31. //p->showpersonage();
  32. //代码崩溃,读取访问权限错误,this指向一个空指针,空的值并没有产生任何属性,所以无法访问m_age
  33. }
  34. int main()
  35. {
  36. test01();
  37. system("pause");
  38. return 0;
  39. }

野指针并无赋值,空指针赋值为空,两者有本质上的区别

2.5友元

2.5.1全局函数做友元

  友元,让一个函数或者类可以访问另一个类中的私有成员。关键字:friend

  比如生活中的客厅允许所有的客人进入,而卧室是私有的,只有自己允许进入,但是也可以允许好朋友进入

注意:

(1)定义友元函数:在类中定义函数的声明,并在前加friend

(2)友元函数主要解决访问类中的私有成员变量的问题

(3)函数可以被定义为多个类的友元函数

(4)友元函数不属于任何类,不是类的成员函数

(5)友元函数不能被const修饰

(6)友元函数可以在类定义的任何地方声明,不受类访问限定符限制

代码实现:

  1. //友元  
  2. //友元目的就是让一个函数或者类访问另一个类中的私有成员  
  3. //关键字friend  
  4. #include<iostream>  
  5. using namespace std;  
  6. class building
  7. {
  8. //通过友元关键字friend,允许homie访问类内私有权限
  9. friend void homie(building* building);
  10. public:
  11. building()
  12. {
  13. m_sittingroom = "公共客厅";
  14. m_bedroom = "私有卧室";
  15. }
  16. public:
  17. string m_sittingroom;
  18. private:
  19. string m_bedroom;
  20. };
  21. void homie(building* building)//全局函数
  22. {
  23. cout << "homie全局函数 正在访问" << building->m_sittingroom << endl;
  24. //cout << "homie全局函数 正在访问" << building->m_bedroom << endl;//单独使用会报错
  25. cout << "homie全局函数 正在访问" << building->m_bedroom << endl;
  26. }
  27. void test01()
  28. {
  29. building building1;
  30. homie(&building1);
  31. }
  32. int main()
  33. {
  34. test01();
  35. system("pause");
  36. return 0;
  37. }

2.5.2友元类

友元类,实际上就是把这个类定义为另一个类的友元(即A类是B类的友元,A可以访问B中的私有成员变量以及保护成员变量)

注意:

(1)友元关系不具有传递性
(2)友元关系不具备双向性(我是你的友元类,我可以访问你,但你不能访问我)
(3)若要相互访问对方类的成员,则要双方都要定义对方为自己的友元
(4)内部类就是外部类的友元类(内部类:如果一个类定义在另一个类的内部,这个内部类就叫做内部类)

代码实现:

  1. #include<iostream>  
  2. using namespace std;  
  3. class building;
  4. class homie
  5. {
  6. public:
  7. homie();//创建构造函数homie,作用是
  8. public:
  9. void visit();//设置函数,访问building中的属性
  10. building* m_building;//这里的building为指针变量
  11. };
  12. class building
  13. {
  14. friend class homie;
  15. //使得homie类可以访问building中的私有成员
  16. public:
  17. building();
  18. public:
  19. string m_sittingroom;//公有客厅
  20. private:
  21. string m_bedroom;//私有卧室
  22. };
  23. //类外写成员函数
  24. building::building()
  25. {
  26. m_sittingroom = "公有客厅";
  27. m_bedroom = "私有卧室";
  28. }
  29. homie::homie()
  30. {
  31. m_building = new building;
  32. //给指针变量赋初值,这个初值为building类型,存放在堆区
  33. }
  34. void homie::visit()
  35. {
  36. cout << "homie类正在访问" << m_building->m_sittingroom << endl;
  37. cout << "homie类正在访问" << m_building->m_bedroom << endl;
  38. }
  39. void test01()
  40. {
  41. homie h1;
  42. h1.visit();
  43. }
  44. int main()
  45. {
  46. test01();
  47. system("pause");
  48. return 0;
  49. }

2.5.3成员函数做友元

注意:需要提前声明要使用的类;必须将先定义的类的成员函数作为后定义的类的友元函数,如果调换顺序会出现语法错误

代码实现:

  1. #include<iostream>  
  2. using namespace std;  
  3. class Building;//前置声明
  4. class homie
  5. {
  6. public:
  7. homie();
  8. ~homie();
  9. void visit_l();
  10. void visit_b();
  11. Building* building;
  12. //指针使用原因:为了building能访问homie的成员函数
  13. //所以需要先写homie,又因为homie需要访问building类,所以只能再前面加上类的声明
  14. //前置声明由于没有实现,所以不能使用对象,只能使用指针
  15. //可以理解为在building类的创建之前,创建了一个类名为building的变量,需要使用指针来指向
  16. };
  17. class Building
  18. {
  19. friend void homie::visit_l();
  20. public:
  21. Building();
  22. public:
  23. string m_livingroom;
  24. private:
  25. string m_bedroom;
  26. };
  27. Building::Building()//类外设置析构函数,初始化
  28. {
  29. m_livingroom = "客厅";
  30. m_bedroom = "卧室";
  31. }
  32. homie::homie()
  33. {
  34. building = new Building;
  35. //创建一个对象Builing在堆区,并且用指针builing维护
  36. //在堆区开辟数据,需要程序员手动释放内存
  37. }
  38. homie::~homie()
  39. {
  40. delete building;
  41. building = NULL;
  42. }
  43. void homie::visit_l()
  44. {
  45. cout << "visit_l正在访问" <<building->m_livingroom << endl;//正在访问building下的客厅
  46. cout << "visit_l正在访问" << building->m_bedroom << endl;
  47. //在Building前设置homie的成员函数visit_l为友元,所以可以访问私有权限下的m_bedroom
  48. }
  49. void homie::visit_b()
  50. {
  51. cout << "visit_b正在访问" << building->m_livingroom << endl;
  52. //cout << "visit_b正在访问" << building->m_bedroom << endl;
  53. //没有使成员函数visit_b做Building的友元,所以building无法访问visit_b函数
  54. }
  55. void test01()
  56. {
  57. homie h1;
  58. h1.visit_l();
  59. h1.visit_b();
  60. }
  61. int main()
  62. {
  63. test01();
  64. system("pause");
  65. return 0;
  66. }

2.6继承

定义不同的类时,下级除了具有自己的特点,还具有上一级的共性,通过继承的技术减少代码重复

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

例如:class java : public normal

对于重复对象,初始基础做法:

  1. #include<iostream>  
  2. using namespace std;  
  3. //以下三个类大部分成员函数相同,仅仅在top部分出现不同
  4. //因此我们可以使用继承的方法,减少重复
  5. class java
  6. {
  7. public:
  8. void base()
  9. {
  10. cout << "代码程序基础" << endl;
  11. }
  12. void rise()
  13. {
  14. cout << "代码程序提高" << endl;
  15. }
  16. void top()
  17. {
  18. cout << "java语言关键" << endl;
  19. }
  20. };
  21. class c
  22. {
  23. public:
  24. void base()
  25. {
  26. cout << "代码程序基础" << endl;
  27. }
  28. void rise()
  29. {
  30. cout << "代码程序提高" << endl;
  31. }
  32. void top()
  33. {
  34. cout << "c语言关键" << endl;
  35. }
  36. };
  37. class python
  38. {
  39. public:
  40. void base()
  41. {
  42. cout << "代码程序基础" << endl;
  43. }
  44. void rise()
  45. {
  46. cout << "代码程序提高" << endl;
  47. }
  48. void top()
  49. {
  50. cout << "python语言关键" << endl;
  51. }
  52. };
  53. void test01()
  54. {
  55. cout << "java" << endl;
  56. java j1;
  57. j1.base();
  58. j1.rise();
  59. j1.top();
  60. cout << "\t" << endl;
  61. cout << "c" << endl;
  62. c c1;
  63. c1.base();
  64. c1.rise();
  65. c1.top();
  66. cout << "\t" << endl;
  67. cout << "python" << endl;
  68. python p1;
  69. p1.base();
  70. p1.rise();
  71. p1.top();
  72. }
  73. int main()
  74. {
  75. test01();
  76. system("pause");
  77. return 0;
  78. }

采用继承的方式,提取公因子,从而减少代码量,提高运行效率,子类也称为派生类,父类也称为基类。派生类包括两大成员:①从基类继承来的共性特点②自己的成员,体现个性特点

以下这段代码,子类为:java、c、python,父类为normal

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. //公共类
  4. class normal
  5. {
  6. public:
  7. void base()
  8. {
  9. cout << "代码程序基础" << endl;
  10. }
  11. void rise()
  12. {
  13. cout << "代码程序提高" << endl;
  14. }
  15. };
  16. //继承实现
  17. class java : public normal
  18. {
  19. public:
  20. void top()
  21. {
  22. cout << "java关键" << endl;
  23. }
  24. };
  25. class c : public normal
  26. {
  27. public:
  28. void top()
  29. {
  30. cout << "c关键" << endl;
  31. }
  32. };
  33. class python : public normal
  34. {
  35. public:
  36. void top()
  37. {
  38. cout << "python关键" << endl;
  39. }
  40. };
  41. //公共类
  42. class normal
  43. {
  44. public:
  45. void base()
  46. {
  47. cout << "代码程序基础" << endl;
  48. }
  49. void rise()
  50. {
  51. cout << "代码程序提高" << endl;
  52. }
  53. };
  54. void test01()
  55. {
  56. cout << "java" << endl;
  57. java j1;
  58. j1.base();
  59. j1.rise();
  60. j1.top();
  61. cout << "\t" << endl;
  62. cout << "c" << endl;
  63. c c1;
  64. c1.base();
  65. c1.rise();
  66. c1.top();
  67. cout << "\t" << endl;
  68. cout << "python" << endl;
  69. python p1;
  70. p1.base();
  71. p1.rise();
  72. p1.top();
  73. }
  74. int main()
  75. {
  76. test01();
  77. system("pause");
  78. return 0;
  79. }

2.6.1继承方式

继承方式分为:公共继承,保护继承,私有继承。与类内的访问权限对应

不同继承方式会导致子类对于父类属性的访问权限变化如图所示

代码实现:

  1. #include<iostream>  
  2. using namespace std;  
  3. //继承方式分为:公共继承,保护继承,私有继承,与类的访问权限一一对应
  4. //不论是哪一种继承方式都无法访问私有继承
  5. //任何一种都可以访问公共继承内容
  6. class father
  7. {
  8. public:
  9. int m_a;
  10. protected:
  11. int m_b;
  12. private:
  13. int m_c;
  14. };
  15. class son1 : public father
  16. {
  17. public:
  18. void func1()
  19. {
  20. m_a = 100;//属于son1的公共权限
  21. m_b = 100;//属于son1的保护权限
  22. //m_c = 100;//属于私有权限,不可访问
  23. }
  24. };
  25. class son2 : protected father
  26. {
  27. public:
  28. void func2()
  29. {
  30. m_a = 50;//father公共权限->son2保护权限
  31. m_b = 50;//father保护权限->son2保护权限
  32. //m_c = 50;//不可访问,属于私有权限
  33. }
  34. };
  35. class son3 : private father
  36. {
  37. public:
  38. void func3()
  39. {
  40. m_a = 10;//属于son3的私有权限
  41. m_b = 10;//属于son3的私有权限
  42. //m_c = 10;//不可访问,属于私有权限
  43. }
  44. };
  45. class grandson : public son1
  46. {
  47. public:
  48. void fun()
  49. {
  50. m_a = 1;//son1公共权限->grandson公共权限
  51. m_b = 1;//son1保护权限->grandson保护权限
  52. }
  53. };
  54. int main()
  55. {
  56. son1 s1;
  57. //cout << s1.m_a << endl;
  58. //cout << s1.m_b << endl;//s1中的m_b为保护权限,无法访问
  59. son2 s2;
  60. //cout << s2.m_a << endl;
  61. //cout << s2.m_b << endl;
  62. //s2中m_a.m_b都是保护权限,无法访问
  63. //同理在son3 s3 中两者都为私有权限,无法访问
  64. system("pause");
  65. return 0;
  66. }

继承中的对象模型:继承中父类所有的属性都会被子类继承下去,只是父类的私有属性被编译器隐藏,无法访问,但是事实存在

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. class father
  4. {
  5. public:
  6. int m_a;
  7. protected:
  8. int m_b;
  9. private:
  10. int m_c;
  11. };
  12. class son : public father
  13. {
  14. public:
  15. int m_d;
  16. };
  17. void test01()
  18. {
  19. son s1;
  20. cout << sizeof(s1) << endl;
  21. }
  22. int main()
  23. {
  24. test01();//16
  25. //说明子类完全继承父类中的属性
  26. //只是父类中的私有属性被编译器隐藏,无法访问,但是他是存在的
  27. system("pause");
  28. return 0;
  29. }

对于上面这串代码通过vs开发人员命令提示符,可以查看son子类的大小以及所含内容,说明其中是继承了父类的所有属性

具体操作:

Cl /d1 reportSingleClassLayout类名 文件名(为报告单个类的布局)

父类与子类的构造与析构函数顺序;

父类构造->子类构造->子类析构->父类析构

2.6.2同名成员处理

  当子类与父类出现同名成员时①访问子类同名成员,直接访问即可②访问父类中的同名成员,加作用域

  代码实现:

  1. //当子类与父类出现同名成员时  
  2. //①访问子类同名成员,直接访问即可  
  3. //②访问父类中的同名成员,加作用域  
  4. #include<iostream>  
  5. using namespace std;  
  6. class father
  7. {
  8. public:
  9. father()
  10. {
  11. m_a = 100;
  12. }
  13. void func()
  14. {
  15. cout << "father-func" << endl;
  16. }
  17. void func(int a)
  18. {
  19. cout << "father-func(int a)" << endl;
  20. }
  21. int m_a;
  22. };
  23. class son : public father
  24. {
  25. public:
  26. son()
  27. {
  28. m_a = 20;
  29. }
  30. void func()
  31. {
  32. cout << "son-func" << endl;
  33. }
  34. int m_a;
  35. };
  36. void test01()
  37. {
  38. //同名属性调用
  39. son s1;
  40. cout << s1.m_a << endl;
  41. //直接访问,得子类中的属性
  42. cout << s1.father::m_a << endl;
  43. //通过子类对象,访问父类中的同名对象
  44. s1.func();//son-func
  45. s1.father::func();//father-func
  46. //如果子类中有与父类同名的成员函数,子类会自动隐藏所有父类同名成员函数,包括函数的重载
  47. s1.father::func(10);//father-func(int a)
  48. }
  49. int main()
  50. {
  51. test01();
  52. system("pause");
  53. return 0;
  54. }

子类对象可以直接访问子类中的同名成员函数;子类对象加作用域可以访问父类中的同名函数;子类对象与父类对象拥有同名函数时,创建的子类对象会自动隐藏父类中的所有同名成员函数,加作用域后即可访问父类中的同名函数

同名静态成员函数与同名静态成员属性与非静态成员函数跟非静态成员属性处理方式相同

多继承语法:一个类继承多个类

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

2.6.3菱形继承

  菱形继承,也叫做钻石继承:①两个派生类(子类)继承同一个基类②某个类同时继承两个派生类

骡是马与驴的杂交种,骡马驴都属于动物,但是骡又同时继承了属于马与骡的特性。马继承了动物的特性,驴继承了动物的特性,当骡使用数据时就会产生二义性。

当使用骡的动物属性时,同时具有马的动物属性与驴的动物属性,然而实际上只需要一份骡的 动物属性即可

需要利用虚继承解决菱形继承的问题

虚继承关键字 virtual,将animal变成虚基类

 

此时m_age只有一份,ma与lv继承的属性为vbptr(虚基类指针virtual base pointer)指向vbtable(虚基类表),不会出现第二份数据

代码实现:

  1. #include<iostream>  
  2. using namespace std;  
  3. //菱形继承概念:
  4. //①两个派生类(子类)继承同一个基类
  5. //②某个类同时继承两个派生类
  6. class animal
  7. {
  8. public:
  9. int m_age;
  10. };
  11. class ma :virtual public animal {};
  12. class lv :virtual public animal {};
  13. class luo:public ma,public lv
  14. {
  15. };
  16. void test01()
  17. {
  18. luo l1;
  19. l1.ma::m_age = 18;
  20. l1.lv::m_age = 26;
  21. cout << l1.ma::m_age << endl;
  22. cout << l1.lv::m_age << endl;
  23. //同时继承了两份数据
  24. //然而luo l1只需要一份数据,导致资源浪费
  25. //加上virtual之后,输出结果都为26
  26. cout << l1.m_age << endl;
  27. //加入虚基类之后就允许直接输出,不会出现二义性,因为为同一份数据
  28. }
  29. int main()
  30. {
  31. test01();
  32. system("pause");
  33. return 0;
  34. }

2.7多态

2.7.1多态基本语法

  多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。

多态分为两类:

静态多态:函数重载与运算符重载属于静态多态

动态多态:派生类和虚函数实现运行时多态

区别:

静态多态函数地址早绑定,编译阶段确定函数地址

动态多态的函数地址晚绑定,运行阶段确定函数地址

代码实现:

  1. #include<iostream>  
  2. using namespace std;  
  3. class animal
  4. {
  5. public:
  6. virtual void speak()
  7. //加入virtual使其变成虚函数,再运行就会打印猫叫
  8. {
  9. cout << "动物说话" << endl;
  10. }
  11. };
  12. class cat:public animal
  13. {
  14. public:
  15. void speak()
  16. {
  17. cout << "猫叫" << endl;
  18. }
  19. };
  20. //执行dospeak函数,地址早绑定,在编译阶段就确定了函数地址
  1. void dospeak01(animal &animal)  //animal &animal =cat
  1. {
  2. animal.speak();
  3. //加入virtual之前不论传入任何类,都会走animal的路径,所以显示动物说话
  4. //speak函数的地址在加入virtual之后无法立刻确认,受传入的不同类,决定不同的路径
  5. }
  6. void test01()
  7. {
  8. cat c1;
  9. dospeak01(c1);
  10. }
  11. int main()
  12. {
  13. test01();//动物说话
  14. system("pause");
  15. return 0;
  16. }

动态多态满足条件:①有继承关系②子类重写父类的虚函数

重写与函数重载不同,重写函数返回值相同,函数名相同,形参列表也相同。但是子类的虚函数可以省略virtual,但在父类中必须要写才能变成虚函数

Animal类内部结构:包含vfptr(虚函数指针)->vftable(虚函数表)

虚函数表的内部会记录一个虚函数的地址&animal::speak

Cat类内部结构:包含vfptr->vftable

虚函数表的内部会记录一个虚函数的地址&animal::speak,因为子类会继承父类中的所有内容

如果子类中重写了父类的虚函数,子类中的vftable(虚函数表)内部会替换成子类的虚函数地址,父类vftable中的内容不改变

当父类的指针或者引用指向子类对象时,就发生了多态//animal &animal =cat,父类的引用指向子类,此时成员函数speak就明确指向cat中的同名speak函数

2.7.2纯虚函数与抽象类

    有时候父类中的虚函数的实现无意义,并不需要,这时候就用纯虚函数代替

语法:virtual 返回值类型 函数名(参数列表)=0;

当类中出现纯虚函数时,这个类就叫抽象类

抽象类特点:①无法实例化对象②子类必须重写抽象类中的纯虚函数,否则子类也为抽象类,无法实例化对象

代码实现:

  1. #include<iostream>  
  2. using namespace std;  
  3. class father
  4. {
  5. public:
  6. virtual void func() = 0;
  7. };
  8. class son:public father
  9. {
  10. public:
  11. virtual void func()
  12. {
  13. cout << "func调用" << endl;
  14. }
  15. };
  16. void test01()
  17. {
  18. //father f1;//抽象类无法实例化对象
  19. son s1;
  20. father* f1 = new son;
  21. f1->func();
  22. }
  23. int main()
  24. {
  25. test01();
  26. system("pause");
  27. return 0;
  28. }

2.7.3虚析构与纯虚析构

多态使用时,如果子类有属性开辟在堆区,那么父类指针释放时无法调用子类的析构函数

这时候将父类的析构函数改为虚析构或者纯虚析构

共性:①可以解决父类指针释放子类对象②都需要具体的函数实现

区别:如果是纯虚析构,那么类属于抽象类,无法实例化对象

问题:

  1. #include<iostream>  
  2. using namespace std;  
  3. class father
  4. {
  5. public:
  6. father()
  7. {
  8. cout << "father构造函数调用" << endl;
  9. }
  10. ~father()
  11. {
  12. cout << "father析构函数调用" << endl;
  13. }
  14. virtual void func() = 0;//纯虚函数
  15. };
  16. class son :public father
  17. {
  18. public:
  19. son(string name)
  20. {
  21. cout << "son构造函数调用" << endl;
  22. m_name = new string(name);
  23. }
  24. ~son()
  25. {
  26. if(m_name != NULL)
  27. {
  28. cout << "son析构函数调用" << endl;
  29. delete m_name;
  30. m_name = NULL;
  31. }
  32. }
  33. void func()
  34. {
  35. cout << "son void func() 调用" << endl;
  36. }
  37. string* m_name;
  38. };
  39. void test01()
  40. {
  41. father* f1 = new son("tom");
  42. //在堆区开辟一块son的内存,并用father类f1接收
  43. f1->func();
  44. delete f1;
  45. }
  46. int main()
  47. {
  48. test01();
  49. system("pause");
  50. return 0;
  51. }

运行结果:

由此可见并没有执行子类的析构函数,并没有释放m_name在堆区的内存,导致内存泄漏

产生原因是由于用父类的指针接收father* f1 = new son("tom");

父类的指针在析构的时候,不会调用子类的析构函数,导致子类如果有堆区数据,无法释放,内存泄露

解决方法:父类的析构前加关键字virtual,使其变成虚析构函数,解决父类释放子类堆区对象不干净问题

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. class father
  4. {
  5. public:
  6. father()
  7. {
  8. cout << "father构造函数调用" << endl;
  9. }
  10. virtual~father()
  11. {
  12. cout << "father析构函数调用" << endl;
  13. }
  14. virtual void func() = 0;//纯虚函数
  15. };
  16. class son :public father
  17. {
  18. public:
  19. son(string name)
  20. {
  21. cout << "son构造函数调用" << endl;
  22. m_name = new string(name);
  23. }
  24. ~son()
  25. {
  26. if(m_name != NULL)
  27. {
  28. cout << "son析构函数调用" << endl;
  29. delete m_name;
  30. m_name = NULL;
  31. }
  32. }
  33. void func()
  34. {
  35. cout << "son void func() 调用" << endl;
  36. }
  37. string* m_name;
  38. };
  39. void test01()
  40. {
  41. father* f1 = new son("tom");
  42. //在堆区开辟一块son的内存,并用father类f1接收
  43. f1->func();
  44. delete f1;
  45. }
  46. int main()
  47. {
  48. test01();
  49. system("pause");
  50. return 0;
  51. }

纯虚析构:virtual ~animal()=0;

纯虚析构的直接调用会导致系统报错,因为此时只是一个函数声明,并没有析构函数的实现

纯虚析构既需要类内声明,也需要类外实现,有了纯虚析构后,所属类也变为抽象类,无法实例化对象

代码实现;

  1. #include<iostream>
  2. using namespace std;
  3. class father
  4. {
  5. public:
  6. father()
  7. {
  8. cout << "father构造函数调用" << endl;
  9. }
  10. //虚析构
  11. //virtual~father()
  12. //{
  13. //  cout << "father析构函数调用" << endl;
  14. //}
  15. //纯虚析构
  16. virtual ~father() = 0;//此时为一个函数的声明,并无实现
  17. virtual void func() = 0;//纯虚函数
  18. };
  19. //纯虚析构的外部函数实现
  20. father:: ~father()
  21. {
  22. cout << "father的纯虚析构函数调用" << endl;
  23. }
  24. class son :public father
  25. {
  26. public:
  27. son(string name)
  28. {
  29. cout << "son构造函数调用" << endl;
  30. m_name = new string(name);
  31. }
  32. ~son()
  33. {
  34. if(m_name != NULL)
  35. {
  36. cout << "son析构函数调用" << endl;
  37. delete m_name;
  38. m_name = NULL;
  39. }
  40. }
  41. void func()
  42. {
  43. cout << "son void func() 调用" << endl;
  44. }
  45. string* m_name;
  46. };
  47. void test01()
  48. {
  49. father* f1 = new son("tom");
  50. //在堆区开辟一块son的内存,并用father类f1接收
  51. f1->func();
  52. delete f1;
  53. }
  54. int main()
  55. {
  56. test01();
  57. system("pause");
  58. return 0;
  59. }

2.8文件操作

2.8.1文本文件

文本文件操作需要头文件<fstream> ofstream写操作 ifstream读操作 fstream读写操作

文件打开方式:

打开方式

解释

Ios::in

读文件而打开

Ios::out

写文件而打开

Ios::ate

初始位置为文件尾部

Ios::app

追加方式写文件

Ios::trunc

如果文件存在,先删除再创建

Ios::binary

二进制方式

二进制方式读文件:int::binary | ios::in

(1)文本文件--写文件

流程:①包含头文件#include<fstream>②创建流对象 ofstream 文件名;③打开文件 文件名.open(“文件路径”,打开方式)④写数据 文件名<<“写入的数据”;⑤关闭文件 文件名.close();

不会在编译器内打印,在未指定状态下,会自动在代码所在文件夹内输出

代码实现:

  1. #include<iostream>  
  2. #include<fstream>  
  3. using namespace std;  
  4. void test01()
  5. {
  6. //创建流对象
  7. ofstream a1;
  8. //指定打开方式
  9. a1.open("test.txt",ios::out );
  10. //写内容
  11. a1 << "姓名:阮晓野" << endl;
  12. a1 << "年龄:18" << endl;
  13. a1.close();
  14. }
  15. int main()
  16. {
  17. test01();
  18. system("pause");
  19. return 0;
  20. }

(2)文本文件—读文件

流程:①包含头文件#include<fstream>②创建流对象 ofstream 文件名;③打开文件 文件名.open(“文件路径”,打开方式)④读数据 四种方式读取⑤关闭文件 文件名.close();

四种方式读取:

//第一种

char buf1[1024] = { 0 };

//初始化一个字符数组,并赋值全为0

while (b1 >> buf1)

//将文本文件中的所有读取到字符数组中

{

cout << buf1 << endl;

}

//第二种

char buf2[1024] = { 0 };

while(b1.getline(buf2,sizeof(buf2)))

//第一个参数为数据传入位置,第二个为读取的字节数

{

cout << buf2 << endl;

}

//第三种

string buf3;

while (getline(b1,buf3))

//全局函数getline需要加头文件string

{

cout << buf3 << endl;

}

//第四种

char c;

while ((c=b1.get())!=EOF)

//get函数每次只读取一个字符,效率低

//EOF end of file 文件尾

{

cout << c ;

}

代码实现:

  1. #include<iostream>
  2. #include<fstream>
  3. #include<string>
  4. using namespace std;
  5. void test01()
  6. {
  7. fstream b1;
  8. b1.open("test.txt", ios::in);
  9. if (!b1.is_open())//判断是否打开成功
  10. {
  11. cout << "文件打开失败" << endl;
  12. return;
  13. }
  14. //读取数据
  15. 第一种
  16. //char buf1[1024] = { 0 };//初始化一个字符数组,并赋值全为0
  17. //while (b1 >> buf1)
  18. 将文本文件中的所有读取到字符数组中
  19. //{
  20. //  cout << buf1 << endl;
  21. //}
  22. 第二种
  23. //char buf2[1024] = { 0 };
  24. //while (b1.getline(buf2,sizeof(buf2)))
  25. 第一个参数为数据传入位置,第二个为读取的字节数
  26. //{
  27. //  cout << buf2 << endl;
  28. //}
  29. 第三种
  30. //string buf3;
  31. //while (getline(b1,buf3))
  32. 全局函数getline需要加头文件string
  33. //{
  34. //  cout << buf3 << endl;
  35. //}
  36. //第四种
  37. char c;
  38. while ((c=b1.get())!=EOF)
  39. //get函数每次只读取一个字符,效率低
  40. //EOF end of file 文件尾
  41. {
  42. cout << c ;
  43. }
  44. b1.close();
  45. }
  46. int main()
  47. {
  48. test01();
  49. system("pause");
  50. return 0;
  51. }

2.8.2二进制文件

打开方式必须指定为ios::binary

(1)二进制方式写文件主要利用流对象调用成员函数write

  代码实现:

  1. #include<iostream>  
  2. #include<fstream>  
  3. using namespace std;  
  4. class person
  5. {
  6. public:
  7. char m_name[64];
  8. int m_age;
  9. };
  10. void test01()
  11. {
  12. ofstream o1;
  13. o1.open("person.txt",ios::out|ios::binary);
  14. person p1 = { "阮晓野",18 };
  15. o1.write((const char *)&p1,sizeof(p1));
  16. //取p1的地址,并且将person类强制转换为const char *
  17. o1.close();
  18. }
  19. int main()
  20. {
  21. test01();
  22. system("pause");
  23. return 0;
  24. }

只能显示姓名,年龄显示为乱码

(2)二进制方式读文件主要利用流对象调用成员函数read

代码实现:

  1. #include<iostream>
  2. #include<fstream>
  3. using namespace std;
  4. class person
  5. {
  6. public:
  7. char m_name[64];
  8. int m_age;
  9. };
  10. void test01()
  11. {
  12. person p1;
  13. ifstream i1;
  14. i1.open("person.txt", ios::in | ios::binary);
  15. if (!i1.is_open())
  16. {
  17. cout << "文件打开失败" << endl;
  18. return;
  19. }
  20. i1.read((char *)&p1,sizeof(p1));
  21. cout << p1.m_name << p1.m_age << endl;
  22. i1.close();
  23. }
  24. int main()
  25. {
  26. test01();
  27. system("pause");
  28. return 0;
  29. }

第三章面向对象应用

3.1矩阵类设计及应用

矩阵类要求:设计一个矩阵类,要求能够根据用户需求构建row行、column列的矩阵,并灵活接受反馈矩阵元素信息(如:某行、某列、某行某列元素)。实现矩阵的相关运算,包括矩阵加(+)、矩阵乘(*)、矩阵输出(<<)、矩阵赋值(=)、获取矩阵指定位置元素值([])。

题目分析:需要在举证类内设置一个二阶数组p

数组运算需要重载运算符(+,*,=)

代码实现:

  1. #include<iostream>
  2. using namespace std;
  3. class jvzheng
  4. {
  5. public:
  6. jvzheng(int colnum,int rownum);
  7. ~jvzheng();
  8. jvzheng& operator=(const jvzheng& m1);//实现矩阵的复制
  9. jvzheng& operator+=(const jvzheng& m2);
  10. jvzheng& operator*=(const jvzheng& m3);
  11. friend istream& operator>>(istream &in ,jvzheng& m4);
  12. jvzheng& operator=(double* a);//实现数组转化为矩阵
  13. void show();//打印矩阵
  14. void show(int r, int c);//打印指定位置元素值
  15. private:
  16. int m_row;//行数
  17. int m_column;//列数
  18. double **p;
  19. void chushihua();//初始化一个矩阵
  20. };
  21. void jvzheng::chushihua()
  22. {
  23. //为数组中的每一个元素在堆区开辟一块内存
  24. p = new double* [m_row];
  25. for (int i = 0; i < m_row ; i++)
  26. {
  27. p[i] = new double [m_column];
  28. }
  29. }
  30. //在构造函数中构建一个全0矩阵
  31. jvzheng::jvzheng(int col,int row)
  32. {
  33. m_row = row;
  34. m_column = col;
  35. chushihua();
  36. if (col< 0)
  37. {
  38. cout << "列数不能为0" << endl;
  39. }
  40. else if (row < 0)
  41. {
  42. cout << "行数不能为0" << endl;
  43. }
  44. else
  45. {
  46. for (int i = 0; i < row; i++)
  47. {
  48. for (int j = 0; j < col; j++)
  49. {
  50. p[i][j] = 0;
  51. }
  52. }
  53. }
  54. }
  55. jvzheng::~jvzheng()
  56. {
  57. for (int i = 0; i < m_row; ++i)
  58. {
  59. delete[] p[i];//释放每一行内存
  60. }
  61. delete[] p;//释放所有内存
  62. }
  63. //实现矩阵的复制
  64. jvzheng& jvzheng::operator=(const jvzheng &m1)
  65. {
  66. if (this == &m1)
  67. {
  68. return *this;
  69. }
  70. if (m_row != m1.m_row || m_column != m1.m_column)
  71. {
  72. for (int i = 0; i < m_row; i++)
  73. {
  74. delete[] p[i];//当出现不一样的时候,删除每一行数据释放空间
  75. }
  76. delete[] p;//释放数组p内存空间
  77. m_row = m1.m_row;
  78. m_column = m1.m_column;
  79. chushihua();
  80. }
  81. for (int i = 0; i < m_row; i++)
  82. {
  83. for (int j = 0; j < m_column; j++)
  84. {
  85. p[i][j] = m1.p[i][j];
  86. }
  87. }
  88. }
  89. //赋值运算符重载
  90. jvzheng& jvzheng::operator=(double* a)
  91. {
  92. for (int i = 0; i < m_row; i++)
  93. {
  94. for (int j = 0; j < m_column; j++)
  95. {
  96. p[i][j] = *(a + i * m_column + j);
  97. }
  98. }
  99. return *this;
  100. }
  101. //加法运算符重构
  102. jvzheng& jvzheng::operator+=(const jvzheng &m2)
  103. {
  104. for (int i = 0; i < m_row; i++)
  105. {
  106. for (int j = 0; j < m_column; j++)
  107. {
  108. p[i][j] += m2.p[i][j];
  109. }
  110. }
  111. return *this;
  112. }
  113. //乘法运算符重构
  114. jvzheng& jvzheng::operator*=(const jvzheng &m3)
  115. {
  116. jvzheng temp(m_row, m3.m_column);//设置一个中间变量用于矩阵乘法 p*m3=temp
  117. for (int i = 0; i < m_row; i++)
  118. {
  119. for (int j = 0; j < m3.m_column; j++)
  120. {
  121. for (int k = 0; k < m_column; k++)
  122. {
  123. temp.p[i][j] = p[i][k] * m3.p[k][j];
  124. }
  125. }
  126. }
  127. *this = temp;
  128. return *this;
  129. }
  130. //显示矩阵
  131. void jvzheng::show()
  132. {
  133. cout << "矩阵行数为:" << m_row << endl;
  134. cout << "矩阵列数为:" << m_column << endl;
  135. cout << "矩阵为:" << endl;
  136. for (int i = 0; i < m_row; i++)
  137. {
  138. for (int j = 0; j < m_column; j++)
  139. {
  140. cout<<p[i][j]<<" ";
  141. }
  142. cout << endl;
  143. }
  144. }
  145. //实现矩阵的输入
  146. istream& operator>>(istream& in,jvzheng& m4)
  147. {
  148. for (int i = 0; i < m4.m_row; i++)
  149. {
  150. for (int j = 0; j < m4.m_column; j++)
  151. {
  152. in >> m4.p[i][j];
  153. }
  154. }
  155. return in;
  156. }
  157. void jvzheng::show(int r, int c)
  158. {
  159. cout << p[r][c] << endl;
  160. }
  161. int main()
  162. {
  163. jvzheng j1(3, 3);
  164. cin >> j1 ;
  165. cout << endl;
  166. jvzheng j2(3, 4);
  167. cin >> j2;
  168. cout << endl;
  169. j1.show();
  170. j1.show(1, 2);
  171. j1 *= j2;
  172. j1.show();
  173. system("pause");
  174. return 0;
  175. }

3.2银行账号管理系统设计及应用

银行账号管理系统要求:管理不同用户在银行的金融资产,每个用户可以拥有多种银行账户(如:定期储蓄、活期储蓄、信息卡、电子账户、贷款账户等)。账户包括账号、余额、利率等基本信息,用户可以进行账户信息查询、存款、取款、结算利息等操作。银行需统计所有账户的总金额、验证银行系统收支平衡,并能够及时预警反馈。设计Account抽象类作为所有银行账户顶层祖先,根据实际应用需求合理设置派生层次及相应子类。结合银行利息结算、用户贷款申请等实际应用需求,适当添加辅助类协同操作。合理定义虚基类、虚函数、纯虚函数、抽象类完成银行账号管理系统的稳定可靠运行。

首先一切的父类就是基础账号,在这个账号中可以实现活期储蓄与定期储蓄,通过成员函数实现账户信息查询,存款,取款,结算利息

可以通过子类设置贷款类,贷款类中除了比父类多出贷款金额与贷款利率,利息的计算,其余基础属性完全相同,所以可以通过继承多态的方式,实现派生

代码实现:

  1. #include<iostream>
  2. #include<ctime>
  3. #define interestRates 0.03//定期利率
  4. #define demandRrates 0.00035//活期利率
  5. #define loanInterest 0.0475//贷款利率
  6. #define creditInterest 0.0055//信用卡利率
  7. using namespace std;
  8. //父类:银行账户类,抽象类
  9. class Account
  10. {
  11. friend class fixedDeposit;
  12. friend class currentDeposit;
  13. public:
  14. virtual void menu();//菜单栏
  15. virtual bool set();//存款
  16. virtual bool get();//取款
  17. virtual void show();//显示账户信息
  18. virtual void setcard(double money, string cardid);//初始化账户
  19. virtual void setid();
  20. virtual void clearingInterest();//利息结算
  21. private:
  22. string m_id;//账号
  23. double m_money;//余额
  24. };
  25. //菜单栏实现
  26. void Account::menu()
  27. {
  28. int choice = 0;
  29. while (true)
  30. {
  31. system("cls");
  32. cout << "····欢迎使用银行账号管理系统····" << endl;
  33. cout << "····请输入功能前的数字·······" << endl;
  34. cout << "····0.退出银行账号管理系统·····" << endl;
  35. cout << "····1.创建个人银行账户·······" << endl;
  36. cout << "····2.查看账户个人信息·······" << endl;
  37. cout << "····3.查询结算利息·········" << endl;
  38. cout << "····4.存款·············" << endl;
  39. cout << "····5.取款·············" << endl;
  40. cin >> choice;
  41. switch (choice)
  42. {
  43. case 0:
  44. {
  45. cout << "已退出银行账号管理系统" << endl;
  46. return;
  47. }
  48. case 1:
  49. {
  50. cout << "创建个人银行账户" << endl;
  51. setid();
  52. break;
  53. }
  54. case 2:
  55. {
  56. cout << "查看个人账户信息" << endl;
  57. show();
  58. break;
  59. }
  60. case 3:
  61. {
  62. cout << "查询结算利息" << endl;
  63. clearingInterest();
  64. break;
  65. }
  66. case 4:
  67. {
  68. cout << "存款" << endl;
  69. set();
  70. break;
  71. }
  72. case 5:
  73. {
  74. cout << "取款" << endl;
  75. get();
  76. break;
  77. }
  78. }
  79. }
  80. }
  81. //实现账户初始化
  82. void Account::setcard(double money, string cardid)
  83. {
  84. m_id = cardid;
  85. m_money = money;
  86. }
  87. //账户设置
  88. void Account::setid()
  89. {
  90. string a1;
  91. cout << "请设置你的id账户:" << endl;
  92. cin >> a1;
  93. cout << "你的id账户为:" << a1 << endl;
  94. m_id = a1;
  95. }
  96. void Account::show()
  97. {
  98. cout << "账户id为:" << m_id << endl;
  99. cout << "账户余额为:" << m_money << endl;
  100. system("pause");
  101. system("cls");
  102. }
  103. //存款
  104. bool Account::set()
  105. {
  106. double inmoney;
  107. char a;
  108. cout << "请输入你的存款金额:" << endl;
  109. cin >> inmoney;
  110. cout << "存款金额为:" << inmoney <<"(t or f)" << endl;
  111. cin >> a;
  112. if (a == 'f' || a == 'F')
  113. {
  114. cout << "存款金额确认有误" << endl;
  115. system("pause");
  116. system("cls");
  117. return false;
  118. }
  119. else
  120. {
  121. m_money = inmoney;
  122. cout << "存款金额确认无误,存款成功" << endl;
  123. }
  124. system("pause");
  125. system("cls");
  126. return true;
  127. }
  128. //取款
  129. bool Account::get()
  130. {
  131. double outmoney;
  132. char a;
  133. cout << "请输入你的取款金额:" << endl;
  134. cin >> outmoney;
  135. if (m_money < outmoney)
  136. {
  137. cout << "余额不足" << endl;
  138. return false;
  139. }
  140. cout << "取款金额为:" << outmoney << "(t or f)" << endl;
  141. cin >> a;
  142. if (a == 'f' || a == 'F')
  143. {
  144. cout << "取款金额确认有误" << endl;
  145. system("pause");
  146. system("cls");
  147. return false;
  148. }
  149. else
  150. {
  151. m_money -= outmoney;
  152. cout << "存款==取款金额确认无误,取款成功" << endl;
  153. }
  154. system("pause");
  155. system("cls");
  156. return true;
  157. }
  158. //实现结算利息
  159. void Account::clearingInterest()
  160. {
  161. double a;
  162. cout << "当前余额为:" << m_money << endl;
  163. cout << "定期利率为:" << interestRates << endl;
  164. a = m_money * interestRates;
  165. cout << "结算定期利息为:" << a << endl;
  166. double b;
  167. cout << "当前余额为:" << m_money << endl;
  168. cout << "活期利率为:" << demandRrates << endl;
  169. b = m_money * demandRrates;
  170. cout << "结算活期利息为:" << b << endl;
  171. system("pause");
  172. system("cls");
  173. }
  174. //贷款账户
  175. class loan :public Account
  176. {
  177. public:
  178. virtual void menu();//菜单栏
  179. virtual bool set();//存款
  180. virtual bool get();//取款
  181. virtual void show();//显示账户信息
  182. virtual void setcard(double money, string cardid);//初始化账户
  183. virtual void setid();
  184. virtual void clearingInterest();//利息结算
  185. virtual bool loanmoney();
  186. private:
  187. string m_id;//账号
  188. double m_money;//余额
  189. double m_loanmoney;
  190. };
  191. void loan::menu()
  192. {
  193. int choice = 0;
  194. while (true)
  195. {
  196. system("cls");
  197. cout << "····欢迎使用银行账号管理系统····" << endl;
  198. cout << "····请输入功能前的数字·······" << endl;
  199. cout << "····0.退出银行账号管理系统·····" << endl;
  200. cout << "····1.创建个人银行账户·······" << endl;
  201. cout << "····2.查看账户个人信息·······" << endl;
  202. cout << "····3.查询结算利息·········" << endl;
  203. cout << "····4.查询存款···········" << endl;
  204. cout << "····5.查询取款···········" << endl;
  205. cout << "····6.查询贷款···········" << endl;
  206. cin >> choice;
  207. switch (choice)
  208. {
  209. case 0:
  210. {
  211. cout << "已退出银行账号管理系统" << endl;
  212. return;
  213. }
  214. case 1:
  215. {
  216. cout << "创建个人银行账户" << endl;
  217. setid();
  218. break;
  219. }
  220. case 2:
  221. {
  222. cout << "查看个人账户信息" << endl;
  223. show();
  224. break;
  225. }
  226. case 3:
  227. {
  228. cout << "查询结算利息" << endl;
  229. clearingInterest();
  230. break;
  231. }
  232. case 4:
  233. {
  234. cout << "查询存款" << endl;
  235. set();
  236. break;
  237. }
  238. case 5:
  239. {
  240. cout << "查询取款" << endl;
  241. get();
  242. break;
  243. }
  244. case 6:
  245. {
  246. cout << "查询贷款" << endl;
  247. loanmoney();
  248. break;
  249. }
  250. }
  251. }
  252. }
  253. //实现账户初始化
  254. void loan::setcard(double money, string cardid)
  255. {
  256. m_id = cardid;
  257. m_money = money;
  258. }
  259. //账户设置
  260. void loan::setid()
  261. {
  262. string a1;
  263. cout << "请设置你的id账户:" << endl;
  264. cin >> a1;
  265. cout << "你的id账户为:" << a1 << endl;
  266. m_id = a1;
  267. system("pause");
  268. system("cls");
  269. }
  270. void loan::show()
  271. {
  272. cout << "账户id为:" << m_id << endl;
  273. cout << "账户余额为:" << m_money << endl;
  274. cout << "贷款金额为:" << m_loanmoney << endl;
  275. system("pause");
  276. system("cls");
  277. }
  278. //存款
  279. bool loan::set()
  280. {
  281. double inmoney;
  282. char a;
  283. cout << "请输入你的存款金额:" << endl;
  284. cin >> inmoney;
  285. cout << "存款金额为:" << inmoney << "(t or f)" << endl;
  286. cin >> a;
  287. if (a == 'f' || a == 'F')
  288. {
  289. cout << "存款金额确认有误" << endl;
  290. system("pause");
  291. system("cls");
  292. return false;
  293. }
  294. else
  295. {
  296. m_money = inmoney;
  297. cout << "存款金额确认无误,存款成功" << endl;
  298. }
  299. system("pause");
  300. system("cls");
  301. return true;
  302. }
  303. //取款
  304. bool loan::get()
  305. {
  306. double outmoney;
  307. char a;
  308. cout << "请输入你的取款金额:" << endl;
  309. cin >> outmoney;
  310. if (m_money < outmoney)
  311. {
  312. cout << "余额不足" << endl;
  313. system("pause");
  314. system("cls");
  315. return false;
  316. }
  317. else
  318. {
  319. cout << "取款金额为:" << outmoney << "(t or f)" << endl;
  320. cin >> a;
  321. if (a == 'f' || a == 'F')
  322. {
  323. cout << "取款金额确认有误" << endl;
  324. system("pause");
  325. system("cls");
  326. return false;
  327. }
  328. else
  329. {
  330. m_money -= outmoney;
  331. cout << "取款金额确认无误,取款成功" << endl;
  332. system("pause");
  333. system("cls");
  334. return true;
  335. }
  336. }
  337. }
  338. //贷款
  339. bool loan::loanmoney()
  340. {
  341. cout << "请输入贷款金额" << endl;
  342. int loan_money;
  343. cin >> loan_money;
  344. char a;
  345. cout << "贷款金额为:" << loan_money <<"(t or f)" << endl;
  346. cin >> a;
  347. if (a=='f'||a=='F')
  348. {
  349. cout << "贷款金额确认有误" << endl;
  350. system("pause");
  351. system("cls");
  352. return false;
  353. }
  354. else
  355. {
  356. m_loanmoney = loan_money;
  357. m_money += m_loanmoney;
  358. cout << "账户余额合计为:" << m_money << endl;
  359. }
  360. system("pause");
  361. system("cls");
  362. return true;
  363. }
  364. //贷款利息结算
  365. void loan::clearingInterest()
  366. {
  367. cout << "贷款利率为" << loanInterest << endl;
  368. double a1;
  369. a1 = m_loanmoney * loanInterest;
  370. cout << "贷款利息为:" << a1 << endl;
  371. system("pause");
  372. system("cls");
  373. }
  374. //信用卡账号
  375. class credit :public Account
  376. {
  377. public:
  378. virtual void menu();//菜单栏
  379. virtual bool set();//存款
  380. virtual bool get();//取款
  381. virtual void show();//显示账户信息
  382. virtual void setcard(double money, string cardid);//初始化账户
  383. virtual void setid();
  384. virtual void clearingInterest();//利息结算
  385. private:
  386. string m_id;//账号
  387. double m_money=0.0;//余额
  388. double m_creditmoney=0.0;//信用透支金额
  389. };
  390. //菜单栏
  391. void credit::menu()
  392. {
  393. int choice = 0;
  394. while (true)
  395. {
  396. system("cls");
  397. cout << "····欢迎使用银行账号管理系统····" << endl;
  398. cout << "····请输入功能前的数字·······" << endl;
  399. cout << "····0.退出银行账号管理系统·····" << endl;
  400. cout << "····1.创建个人银行账户·······" << endl;
  401. cout << "····2.查看账户个人信息·······" << endl;
  402. cout << "····3.查询结算利息·········" << endl;
  403. cout << "····4.查询存款···········" << endl;
  404. cout << "····5.查询取款···········" << endl;
  405. cin >> choice;
  406. switch (choice)
  407. {
  408. case 0:
  409. {
  410. cout << "已退出银行账号管理系统" << endl;
  411. return;
  412. }
  413. case 1:
  414. {
  415. cout << "创建个人银行账户" << endl;
  416. setid();
  417. break;
  418. }
  419. case 2:
  420. {
  421. cout << "查看个人账户信息" << endl;
  422. show();
  423. break;
  424. }
  425. case 3:
  426. {
  427. cout << "查询结算利息" << endl;
  428. clearingInterest();
  429. break;
  430. }
  431. case 4:
  432. {
  433. cout << "查询存款" << endl;
  434. set();
  435. break;
  436. }
  437. case 5:
  438. {
  439. cout << "查询取款" << endl;
  440. get();
  441. break;
  442. }
  443. }
  444. }
  445. }
  446. //实现账户初始化
  447. void credit::setcard(double money, string cardid)
  448. {
  449. m_id = cardid;
  450. m_money = money;
  451. }
  452. //账户设置
  453. void credit::setid()
  454. {
  455. string a1;
  456. cout << "请设置你的id账户:" << endl;
  457. cin >> a1;
  458. cout << "你的id账户为:" << a1 << endl;
  459. m_id = a1;
  460. system("pause");
  461. system("cls");
  462. }
  463. void credit::show()
  464. {
  465. cout << "账户id为:" << m_id << endl;
  466. cout << "账户余额为:" << m_money << endl;
  467. cout << "信用透支金额为:" << m_creditmoney << endl;
  468. system("pause");
  469. system("cls");
  470. }
  471. //存款
  472. bool credit::set()
  473. {
  474. double inmoney;
  475. char a;
  476. cout << "请输入你的存款金额:" << endl;
  477. cin >> inmoney;
  478. cout << "存款金额为:" << inmoney << "(t or f)" << endl;
  479. cin >> a;
  480. if (a == 'f' || a == 'F')
  481. {
  482. cout << "存款金额确认有误" << endl;
  483. system("pause");
  484. system("cls");
  485. return false;
  486. }
  487. else
  488. {
  489. if (m_creditmoney != 0)
  490. {
  491. cout << "当前透支金额为:" << m_creditmoney << endl;
  492. char ch;
  493. cout << "是否优先还款(y or n)" << endl;
  494. cin >> ch;
  495. if (ch=='y'||ch=='Y')
  496. {
  497. cout << "优先还款" << endl;
  498. m_money = inmoney - m_creditmoney;
  499. system("pause");
  500. system("cls");
  501. return true;
  502. }
  503. else if(ch=='n'||ch=='N')
  504. {
  505. cout << "非优先还款" << endl;
  506. m_money = inmoney;
  507. cout << "当前存款金额为:" << m_money << endl;
  508. system("pause");
  509. system("cls");
  510. return false;
  511. }
  512. }
  513. else
  514. {
  515. cout << "无信用透支金额" << endl;
  516. m_money = inmoney;
  517. cout << "存款金额确认无误,存款成功" << endl;
  518. system("pause");
  519. system("cls");
  520. return true;
  521. }
  522. }
  523. }
  524. //取款
  525. bool credit::get()
  526. {
  527. double outmoney;
  528. char a;
  529. cout << "请输入你的取款金额:" << endl;
  530. cin >> outmoney;
  531. if (m_money < outmoney)
  532. {
  533. cout << "余额不足" << endl;
  534. cout << "是否透支信用卡(y or n)" << endl;
  535. char ch;
  536. cin >> ch;
  537. if (ch == 'n' || ch == 'N')
  538. {
  539. cout << "余额不足取款失败" << endl;
  540. return false;
  541. }
  542. else if(ch=='y'||ch=='Y')
  543. {
  544. cout << "取款金额为:" << outmoney << "(t or f)" << endl;
  545. cin >> a;
  546. if (a == 'f' || a == 'F')
  547. {
  548. cout << "取款金额确认有误" << endl;
  549. system("pause");
  550. system("cls");
  551. return false;
  552. }
  553. else if (a == 't' || a == 'T')
  554. {
  555. m_creditmoney = outmoney;
  556. cout << "取款金额确认无误,取款成功" << endl;
  557. }
  558. system("pause");
  559. system("cls");
  560. return true;
  561. }
  562. }
  563. else
  564. {
  565. if (m_creditmoney != 0)
  566. {
  567. cout << "当前透支金额为:" << m_creditmoney << endl;
  568. double a;
  569. a = m_creditmoney * creditInterest;
  570. cout << "透支金额利息结算为:" << a << endl;
  571. m_money -= m_creditmoney * creditInterest;
  572. cout << "当前余额为:" << m_money << endl;
  573. cout << "取款金额为:" << outmoney << "(t or f)" << endl;
  574. cin >> a;
  575. if (a == 'f' || a == 'F')
  576. {
  577. cout << "取款金额确认有误" << endl;
  578. system("pause");
  579. system("cls");
  580. return false;
  581. }
  582. else if (a == 't' || a == 'T')
  583. {
  584. m_money -= outmoney;
  585. cout << "取款金额确认无误,取款成功" << endl;
  586. cout << "当前余额为:" << m_money << endl;
  587. system("pause");
  588. system("cls");
  589. return true;
  590. }
  591. }
  592. else
  593. {
  594. cout << "当前无透支金额" << endl;
  595. cout << "取款金额为:" << outmoney << "(t or f)" << endl;
  596. cin >> a;
  597. if (a == 'f' || a == 'F')
  598. {
  599. cout << "取款金额确认有误" << endl;
  600. system("pause");
  601. system("cls");
  602. return false;
  603. }
  604. else if (a == 't' || a == 'T')
  605. {
  606. m_creditmoney = outmoney;
  607. cout << "取款金额确认无误,取款成功" << endl;
  608. system("pause");
  609. system("cls");
  610. return true;
  611. }
  612. }
  613. }
  614. }
  615. void credit::clearingInterest()
  616. {
  617. cout << "信用卡利息为:" << creditInterest << endl;
  618. double a;
  619. a = m_creditmoney * creditInterest;
  620. cout << "信用透支金额为:" << m_creditmoney << endl;
  621. cout << "透支金额利息结算为:" << a << endl;
  622. system("pause");
  623. system("cls");
  624. }
  625. void test01()
  626. {
  627. Account a1;
  628. a1.menu();
  629. }
  630. void test02()
  631. {
  632. loan l1;
  633. l1.menu();
  634. }
  635. void test03()
  636. {
  637. credit c1;
  638. c1.menu();
  639. }
  640. int main()
  641. {
  642. //test01();
  643. //test02();
  644. test03();
  645. system("pause");
  646. return 0;
  647. }

结束语

在写这份大作业的同时,我回顾了之前的所学内容,在原有理解的基础上,有了新的感悟与体验,对于程序本身的运行逻辑有了更深的感悟。

在学习面向对象程序设计的过程中,充分使用了c语言的基础,并且在此基础上扩展了类的用法,通过对类属性的抽象,实现父类子类的继承关系以及向下发展的多态多样化。在面对更加具体的要求时,可以实现具体的实例化,解决实际问题。同时通过面向对象程序设计的继承与多态特点,可以简明的实现类似功能,以及功能的差异化。虚基类,纯虚基类更是在细化的基础上,很大程度的减少了内存空间的占用,避免了无用操作导致程序运行时间变成的问题。在类的学习过程中,我认为类与结构体的学习过程十分相似,都有统一的格式要求,并且在内部有一定的特定功能,与结构体不同的是,类在实现功能与属性定义的同时,还可以对访问权限进行限制,不仅提高了代码的可读性,更方便了程序运行与定义。

通过析构函数构造函数拷贝构造函数,方便了数据在堆区的开辟与释放,同时实现了深层拷贝,避免了浅拷贝所带来的内存重复释放问题

最后学习了类文件操作,打开了对新世界的大门,知道了二进制文件与文本文件的区别,以及如何编写,进行文件操作,丰富了编程的手段与形式,扩展了输出表达的方式。

在面向对象程序设计的学习过程中,十分感谢刘宁老师的答疑解惑,解决了很多我不能理解的问题,从而让我对面向对象程序设计的研究学习更加深入。

《面向对象程序设计》相关推荐

  1. ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  2. ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  3. 信息学奥赛真题解析(玩具谜题)

    玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...

  4. 信息学奥赛之初赛 第1轮 讲解(01-08课)

    信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...

  5. 信息学奥赛一本通习题答案(五)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  6. 信息学奥赛一本通习题答案(三)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  7. 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题

    第1章   快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章  素数 第 3 章  约数 第 4 章  同余问题 第 5 章  矩阵乘法 第 6 章 ...

  8. 信息学奥赛一本通题目代码(非题库)

    为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...

  9. 信息学奥赛一本通(C++版) 刷题 记录

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...

  10. 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离

    首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...

最新文章

  1. AI项目成功的4要素
  2. Forth Week :快速上手一门编程语言
  3. Android开发把项目打包成apk
  4. html怎么显示直线,html怎么用鼠标画出一条直线,鼠标移动时候要能看到线条
  5. 成都理工大学c语言复试,2020年成都理工大学信号与信息处理考研真题试卷及试题答案,C语言程序设计考研试题下载...
  6. Java 8流:Micro Katas
  7. 面向空天地一体多接入的融合6G网络架构展望
  8. 矩阵论思维导图_《实变函数论》 江泽坚 3rd 思维导图与笔记整理
  9. termux php 出错,android上的终端——termux
  10. 3-3-ServletContext接口
  11. 基于图像界面工具postman进行测试
  12. 批处理命令启动和关闭tomcat
  13. 登陆服务器老出现“达到最大连接数解决方法
  14. 服装CAD计算机按钮在哪里,富仪服装CAD快捷键大全
  15. IEEE1588v2解析(7)gPTP协议和PTP的关系
  16. Maix_Bit官方烧录软件和IDE的使用
  17. hourglass论文_Stacked Hourglass networks
  18. 【答粉丝问】桌面运维需要具备的技能有什么?
  19. 7.计蒜客ACM题库.A2233 结果填空:钟表
  20. 杂谈:电商平台中的图片资源优化实战

热门文章

  1. Bearer token Java怎么解析
  2. Node.JS实战60:解除“封印”!给Node更多的内存。
  3. 蓝桥杯刷题之分享或许会迟到,但绝不会缺席
  4. 车路协同实现第二次技术革新 觉非科技路侧融合感知系统知寰™正式发布
  5. 中留服认证,英国名校本科、硕士学位,汉院报考即得3000英镑奖学金,你还不心动吗?
  6. mysql xp_【Mysql5.5 XP系统下载】mysql XP系统安装图解
  7. Win7中,文件夹有个小锁什么意思??的解决
  8. gitHub Action - workflow 概念和基本操作
  9. Java高级编程架构——Spring实战:Spring初探
  10. Mosh_完全掌握SQL【笔记】