软件需求请点击此处留言菜鸟资源为你准备 立即查看

第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

接第十四章继续讲解 8.4.4. 组装电脑系统 原理同上。依赖倒置原则。 8.4.5. 企业员工信息管理系统 8.4.5.1. 需求 一个小型公司的人员信息管理系统 某小型公司,主要有四类人员:经理、技术人员、销售经理和推销员。现在,需要存...

当前版本

软件大小

软件语言

是否破解

接第十四章继续讲解

第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

8.4.4. 组装电脑系统

原理同上。依赖倒置原则。

  1. #include <iostream>
  2. using namespace std;
  3. class HardDisk
  4. {
  5. public:
  6. virtual void run() = 0;
  7. };
  8. class Memory
  9. {
  10. public:
  11. virtual void run() = 0;
  12. };
  13. class Cpu
  14. {
  15. public:
  16. virtual void run() =0 ;
  17. };
  18. class WDHardDisk:public HardDisk
  19. {
  20. public:
  21. void run()
  22. {
  23. cout<<"我是西数硬盘,500g 5400r/m"<<endl;
  24. }
  25. };
  26. class IntelCpu:public Cpu
  27. {
  28. public:
  29. void run()
  30. {
  31. cout<<"我是 intel Cpu,3.4gh "<<endl;
  32. }
  33. };
  34. class kingStonMem:public Memory
  35. {
  36. public:
  37. void run()
  38. {
  39. cout<<"我是金士顿内存,16g 1333"<<endl;
  40. }
  41. };
  42. class Computer
  43. {
  44. public:
  45. Computer( Cpu *c,Memory *m,HardDisk *d)
  46. :disk(d),mem(m),cpu(c){}
  47. void work()
  48. {
  49. cpu->run();
  50. mem->run();
  51. disk->run();
  52. }
  53. private:
  54. HardDisk *disk;
  55. Memory * mem;
  56. Cpu * cpu;
  57. };
  58. int main()
  59. {
  60. IntelCpu *pc = new IntelCpu;
  61. kingStonMem *pm = new kingStonMem;
  62. WDHardDisk *ph = new WDHardDisk;
  63. Computer cpt(pc,pm,ph);
  64. cpt.work();
  65. delete pc;
  66. delete pm;
  67. delete ph;
  68. return 0;
  69. }

8.4.5. 企业员工信息管理系统

8.4.5.1. 需求

一个小型公司的人员信息管理系统

某小型公司,主要有四类人员:经理、技术人员、销售经理和推销员。现在,需要存储这些人员的姓名、编号、级别、当月薪水.计算月薪总额并显示全部信息。

人员编号基数为 1000,每输入一个人员信息编号顺序加 1。

程序要有对所有人员提升级别的功能。本例中为简单起见,所有人员的初始级别均为 1 级。然后进行升级,经理升为 4 级,技术人员和销售经理升为 3 级,推销员仍为 1级。

月薪计算办法是:经理拿固定月薪 8000 元;技术人员按每小时 100 元领取月薪;推销员的月薪按该推销员当月销售额的 4%提成;销售经理既拿固定月薪也领取销售提成,固定月薪为 5000 元,销售提成为所管辖部门当月销售总额的 5%。

8.4.5.2. 详细设计

父类

属性:

string 姓名、int 编号、int 级别、foat 当月薪水

static starNum;

行为:

void getPay()计算月薪总额

void disInfor()显示全部信息

8.4.5.3. 类设计

第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

8.4.5.4.

代码实现

 employee.h

  1. #ifndef EMPLOYEE_H
  2. #define EMPLOYEE_H
  3. #include <iostream>
  4. using namespace std;
  5. class Employee
  6. {
  7. public:
  8. Employee();
  9. virtual ~Employee();
  10. virtual void promote(int increment = 0) =0;
  11. virtual void getPay() = 0;
  12. virtual void disInfor() = 0;
  13. protected:
  14. string name;
  15. int num;
  16. int grade;
  17. float salary;
  18. static int startNum;
  19. };
  20. #endif // EMPLOYEE_H

 manager.h

  1. #ifndef MANAGER_H
  2. #define MANAGER_H
  3. #include "employee.h"
  4. class Manager:virtual public Employee
  5. {
  6. public:
  7. Manager();
  8. ~Manager();
  9. void promote(int increment = 0);
  10. void getPay();
  11. void disInfor();
  12. protected:
  13. float fixSalay;
  14. };
  15. #endif // MANAGER_H

 technician.h

  1. #ifndef TECHNICIAN_H
  2. #define TECHNICIAN_H
  3. #include "employee.h"
  4. class Technician:public Employee
  5. {
  6. public:
  7. Technician();
  8. ~Technician();
  9. virtual void promote(int increment = 0) ;
  10. virtual void getPay() ;
  11. virtual void disInfor() ;
  12. private:
  13. int moneyPerHour;
  14. int hourCount;
  15. };
  16. #endif // TECHNICIAN_H

 saleman.h

  1. #ifndef SALESMAN_H
  2. #define SALESMAN_H
  3. #include "employee.h"
  4. class SalesMan:virtual public Employee
  5. {
  6. public:
  7. SalesMan();
  8. ~SalesMan();
  9. virtual void promote(int increment = 0) ;
  10. virtual void getPay();
  11. virtual void disInfor();
  12. protected:
  13. float saleAmount;
  14. float persent;
  15. };
  16. #endif // SALESMAN_H

 salesmanager.h

  1. #ifndef SALEMANAGER_H
  2. #define SALEMANAGER_H
  3. #include "salesman.h"
  4. #include "manager.h"
  5. class SaleManager:public SalesMan,public Manager
  6. {
  7. public:
  8. SaleManager();
  9. ~SaleManager();
  10. void promote(int increment = 0);
  11. void getPay();
  12. void disInfor();
  13. };
  14. #endif // SALEMANAGER_H

 main.cpp

  1. #include <iostream>
  2. #include "employee.h"
  3. #include "technician.h"
  4. #include "manager.h"
  5. #include "salesman.h"
  6. #include "salemanager.h"
  7. using namespace std;
  8. int main()
  9. {
  10. Employee * p[] = {new Manager,new Technician,,new SalesMan,new SaleManager};
  11. for(int i=0; i<sizeof(p)/sizeof(p[0]); i++)
  12. {
  13. p[i]->promote(i);
  14. p[i]->getPay();
  15. cout<<"-----------------"<<endl;
  16. p[i]->disInfor();
  17. cout<<"-----------------"<<endl;
  18. }
  19. return 0;
  20. }

优化补充:

8.4.6. cocos跨平台入口分析

8.4.6.1. 代跨平台设计原理

第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

8.4.6.2. 类设计

8.4.6.3. 代码实现

 CCApplicationProtocol.h

  1. #ifndef CCAPPLICATIONPROTOCOL_H_
  2. #define CCAPPLICATIONPROTOCOL_H_
  3. class CCApplicationProtocol {
  4. public:
  5. CCApplicationProtocol(){}
  6. virtual ~CCApplicationProtocol(){}
  7. virtual bool applicationDidFinishLaunching() = 0;
  8. };
  9. #endif /* CCAPPLICATIONPROTOCOL_H_ */

 AppDelegate.h

  1. #ifndef APPDELEGATE_H_
  2. #define APPDELEGATE_H_
  3. #include "CCApplication.h"
  4. class AppDelegate: private CCApplication {
  5. public:
  6. AppDelegate();
  7. virtual ~AppDelegate();
  8. bool applicationDidFinishLaunching();
  9. };
  10. #endif /* APPDELEGATE_H_ */

 AppDelegate.cpp

  1. #include "AppDelegate.h"
  2. #include "iostream"
  3. using namespace std;
  4. AppDelegate::AppDelegate() {
  5. // TODO Auto-generated constructor stub
  6. }
  7. AppDelegate::~AppDelegate() {
  8. // TODO Auto-generated destructor stub
  9. }
  10. bool AppDelegate::applicationDidFinishLaunching()
  11. {
  12. cout<<"star game"<<endl;
  13. }

 CCApplication.h

  1. #ifndef CCAPPLICATION_H_
  2. #define CCAPPLICATION_H_
  3. #include "CCApplicationProtocol.h"
  4. class CCApplication: public CCApplicationProtocol {
  5. public:
  6. CCApplication();
  7. virtual ~CCApplication();
  8. int run();
  9. static CCApplication * sharedApplication();
  10. static CCApplication * sm_pSharedApplication;
  11. };
  12. #endif /* CCAPPLICATION_H_ */

 CCApplication.cpp

  1. #include "CCApplication.h"
  2. #include "stddef.h"
  3. CCApplication * CCApplication::sm_pSharedApplication = NULL;
  4. CCApplication::CCApplication() {
  5. // TODO Auto-generated constructor stub
  6. sm_pSharedApplication = this;
  7. }
  8. CCApplication::~CCApplication() {
  9. // TODO Auto-generated destructor stub
  10. }
  11. CCApplication * CCApplication::sharedApplication()
  12. {
  13. if(sm_pSharedApplication != NULL)
  14. return sm_pSharedApplication;
  15. }
  16. int CCApplication::run()
  17. {
  18. applicationDidFinishLaunching();
  19. }

 main.cpp

  1. #include "CCApplication.h"
  2. int main()
  3. {
  4. AppDelegate app;
  5. return CCApplication::sharedApplication()->run();
  6. }

8.5. 多态实现浅浅析

8.5.1. 虚函数表

C++的多态是通过一张虚函数表(Virtual Table)来实现的,简称为 V-Table。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。 这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。

假设我们有这样的一个类:

  1. class Base {
  2. public:
  3. virtual void f() { cout << "Base::f" << endl; }
  4. virtual void g() { cout << "Base::g" << endl; }
  5. virtual void h() { cout << "Base::h" << endl; }
  6. };

按照上面的说法,我们可以通过 Base 的实例来得到虚函数表。 下面是实际例程:

  1. int main()
  2. {
  3. cout<<sizeof(Base)<<endl; //比平时多出 4 个字节。
  4. typedef void(*Fun)(void);
  5. Base b;
  6. Fun pFun = NULL;
  7. cout << "虚函数表地址:" << (int*)(&b) << endl;
  8. cout << "虚函数表第一个函数地址:" << (int*)*(int*)(&b) << endl;
  9. // Invoke the first virtual function
  10. pFun = (Fun)*((int*)*(int*)(&b));
  11. pFun();
  12. return 0;
  13. }

实际运行经果如下

  1. 虚函数表地址:0012FED4
  2. 虚函数表 第一个函数地址:0044F148
  3. Base::f

通过这个示例,我们可以看到,我们可以通过强行把&b 转成 int *,取得虚函数表的地

址,然后,再次取址就可以得到第一个虚函数的地址了,也就是 Base::f(),这在上面的程序

中得到了验证(把 int* 强制转成了函数指针)。通过这个示例,我们就可以知道如果要调

用 Base::g()和 Base::h(),其代码如下:

  1. (Fun)*((int*)*(int*)(&b)+0); // Base::f()
  2. (Fun)*((int*)*(int*)(&b)+1); // Base::g()
  3. (Fun)*((int*)*(int*)(&b)+2); // Base::h()

画个图解释一下。如下所示:

第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

注意:在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“/0”一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。

下面,我将分别说明“无覆写”和“有覆写”时的虚函数表的样子。没有覆写父类的虚函数是毫无意义的。我之所以要讲述没有覆写的情况,主要目的是为了给一个对比。在比较之下,我们可以更加清楚地知道其内部的具体实现。

8.5.2. 一般继承(无虚函数覆写)

下面,再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系:

第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

请注意,在这个继承关系中,子类没有重载任何父类的函数。那么,在派生类的实例中,其虚函数表如下所示:

对于实例:Derive d; 的虚函数表如下:

第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

我们可以看到下面几点:

1)虚函数按照其声明顺序放于表中。

2)父类的虚函数在子类的虚函数前面

8.5.3. 一般继承(有虚函数覆写)

覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子?假设,我们有下面这样的一个继承关系。

第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

为了让大家看到被继承过后的效果,在这个类的设计中,我只覆盖了父类的一个函数:f()。那么,对于派生类的实例,其虚函数表会是下面的一个样子:

第十五章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

我们从表中可以看到下面几点,

1)覆写的 f()函数被放到了虚表中原来父类虚函数的位置。

2)没有被覆盖的函数依旧。这样,我们就可以看到对于下面这样的程序,

  1. Base *b = new Derive();
  2. b->f();

由 b 所指的内存中的虚函数表的 f()的位置已经被 Derive::f()函数地址所取代,于是在实际调用发生时,是 Derive::f()被调用了。这就实现了多态。

8.5.4. 静态代码发生了什么?

  1. Base *b = new Derive();
  2. b->f();

当编译器看到这段代码的时候,并不知道 b 真实身份。编译器能作的就是用一段代码

代替这段语句。

1,明确 b 类型。

2,然后能过指针虚函数表的指针 vptr 和偏移量,匹配虚函数的入口。

3,根据入口地址调用虚函数。

8.5.5. 评价多态

1,实现在动态绑定。

2,些许的牺牲了一些空间和效率。

8.5.6. 常见问答

 为什么虚函数必须是类的成员函数:

虚函数诞生的目的就是为了实现多态,在类外定义虚函数毫无实际用处。

 为什么类的静态成员函数不能为虚函数:

如果定义为虚函数,那么它就是动态绑定的,也就是在派生类中可以被覆盖的,这与静态成员函数的定义(:在内存中只有一份拷贝;通过类名或对象引用访问静态成员)本身就是相矛盾的。

 为什么构造函数不能为虚函数:

因为如果构造函数为虚函数的话,它将在执行期间被构造,而执行期则需要对象已经建立,构造函数所完成的工作就是为了建立合适的对象,因此在没有构建好的对象上不可能执行多态(虚函数的目的就在于实现多态性)的工作。在继承体系中,构造的顺序就是从基类到派生类,其目的就在于确保对象能够成功地构建。构造函数同时承担着虚函数表的建立,如果它本身都是虚函数的话,如何确保 vtbl 的构建成功呢?

注意:当基类的构造函数内部有虚函数时,会出现什么情况呢?结果是在构造函数140中,虚函数机制不起作用了,调用虚函数如同调用一般的成员函数一样。当基类的析构函数内部有虚函数时,又如何工作呢?与构造函数相同,只有“局部”的版本被调用。但是,行为相同,原因是不一样的。构造函数只能调用“局部”版本,是因为调用时还没有派生类版本的信息。析构函数则是因为派生类版本的信息已经不可靠了。我们知道,析构函数的调用顺序与构造函数相反,是从派生类的析构函数到基类的析构函数。当某个类的析构函数被调用时,其派生类的析构函数已经被调用了,相应的数据也已被丢失,如果再调用虚函数的派生类的版本,就相当于对一些不可靠的数据进行操作,这是非常危险的。因此,在析构函数中,虚函数机制也是不起作用的

  1. #include <iostream>
  2. using namespace std;
  3. class A
  4. {
  5. public:
  6. A()
  7. {
  8. p = this;
  9. p->func();
  10. }
  11. virtual void func()
  12. {
  13. cout<<"aaaaaaaaaaaaaaaa"<<endl;
  14. }
  15. private:
  16. A *p;
  17. };
  18. class B:public A
  19. {
  20. public:
  21. void func()
  22. {
  23. cout<<"bbbbbbbbbbbbbbbbb"<<endl;
  24. }
  25. };
  26. int main()
  27. {
  28. B b;
  29. return 0;
  30. }

本章完,下一大章我们讲解模板(Templates)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

常见问题

  • 如何安装和激活?

    每款软件会附带有安装教程,您打开安装包一目了然

  • 程序是否已损坏?文件损坏?还是其他错误?

    有错误问题,请您参考:https://www.xxx.com/error

  • 如何更新?正式激活后会发生什么情况?

    除非安装说明中另有说明,否则官方更新可能会导致无法激活。 要从本网站更新软件,您需要在此处下载此软件的新版本(如果可用),并将其安装在计算机上安装的版本之上(替换)。在这种情况下,您将保存此软件的激活和设置。

  • 如何下载?链接不起作用?

    我们使用百度网盘,132云盘和微软网盘,除了百度网盘,其他两款不限速,如果链接失效,请您联系客服处理

  • 已发布更新。我什么时候升级我的版本?

    所有软件如有更新,我们第一时间推送,视自己情况更新使用

  • 如何更改语言?

    打开“系统偏好设置”->“通用>语言和地区”->应用程序-“+”。 选择应用和语言。此方法适用于大多数应用程序。 Adobe 产品中的语言通常是在产品本身的安装阶段选择的。 游戏中的语言通常会在游戏本身的设置中发生变化。

  • 如何删除软件?

    有很多选择。最简单的方法是使用特殊的实用程序来卸载应用程序,例如App Cleaner Uninstaller 要删除 Adobe 产品,请使用 Creative Cloud Cleaner Tool

  • 需要远程帮助吗?

    网站已开通永久会员的可享受免费远程,如果非永久会员,远程安装另外收费

点点赞赏,手留余香

给TA打赏
共0人
还没有人赞赏,快来当第一个赞赏的人吧!
    技术教程

    第十四章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

    2024-2-3 9:32:22

    技术教程

    第十六章丨C++编程宝典:快速上手、深入进阶、挑战高级技巧,助你成为编程达人

    2024-2-5 9:31:00

    0 条回复 A文章作者 M管理员
    人生如逆旅,我亦是行人
    欢迎您,新朋友,感谢参与互动!
      暂无讨论,说说你的看法吧
    后退
    榜单