第九节 面向对象程序设计
1. 面向过程程序设计:以数据流为基础,模块化系统设计,以寻找问题解决过程为主题的设计方式;数据结构是算法的辅助
// 面向过程的程序设计: 分析问题,解决问题 // 分析问题:将大问题拆分为子问题,分析出问题所解决的事情,问题给出的条件,以及问题解决后需要的反馈 // 解决问题:设计具体的步骤,解决问题 // 对学生成绩进行排序 // 输入:学生的成绩,学生的信息 // 输出:排序后的学生成绩 struct StudentInfo { int StudentID; float Score; }; // 解决问题,设计算法 // 起泡排序 void BubbleSort (StudentInfo students[], int len) { StudentInfo temp; for (int i = 0; i < len - 1; i++) for (int j = 0; j < len - 1 - i; j++) { if (students[j].Score > students[j + 1].Score) { temp = students[j]; students[j] = students[j + 1]; students[j + 1] = temp; } } } // 完成汉诺塔设计(将盘子从A移动到C) // 输入:起始条件,A柱上盘子的数量n // 输出:所有盘子移动到C柱上 // 解决问题,设计算法 // 把n-1个盘子由 A 移到 B // 把第n个盘子由 A 移到 C // 把n-1个盘子由 B 移到 C struct Pillar {}; void Move(Pillar from, Pillar to); void Hanoi(int n, Pillar A, Pillar B, Pillar C) { if (n == 1) Move(A, C); else { Hanoi(n - 1, A, C, B); Move(A, C); Hanoi(n - 1, B, A, C); } } // 设计学生管理系统 // 问题分解:学生管理、课程管理、成绩管理 // 问题分解_学生管理:学生入学,学生信息录入 // 问题分解_课程管理:创建课程,选课 // 问题分解_成绩管理:成绩录入 struct Student; struct Course; void CreateStudent(); void CreateCourse(); void InputStudentInformation(); void AddCourse(); void InputCourseMark(); //... // 完成汽车自动化生产 // 问题分解:材料准备、汽车组装、汽车测试 // 问题分解_汽车测试:测试刹车、测试发动机、测试反光镜、测试水箱.... // 解决问题:测试刹车 // 输入:刹车信号 // 输出:汽车停止时间 // 解决问题:测试发送机 // 输入:油门信号 // 输出:百米百公里加速时间 // .......
2. 面向对象程序设计:以对象(Object)为基础设计系统;通过为对象添加属性(变量)、功能(方法),并研究对象之间的关系,从而解决问题
//以面向对象的设计方式,设计学生管理系统 //1. 明确程序中的对象 //2. 明确不同对象间的联系 //3. 为对象设计属性与方法 class Student { private: Course* Courses; //...Informations public: void AddCourse(); //Getter/Setters for Informations //... //Create Student(); //Dispose ~Student(); }; class Course { private: double _mark; //...Course Informations public: double GetMark(); double SetMark(); //Getter/Setters for Course Informations //... //Create Course(); //Dispose ~Course(); };
3. 类与数据抽象化
#include <iostream> #include <string> #include <list> using namespace std; // 在进行面向过程的(模块化)的系统设计时,根据算法设计合适的结构体(数据集合) // 此时,数据与函数是分离的关系 struct StudentInfo { string Name; int Age; string Gender; string Major; string Department; }; void PrintStudentInfo(); void GetStudentInfoByMajor(string major); StudentInfo* OrderStudentByAge(); // 在进行面向对象设计时,首先要做的就是对数据进行抽象 // 在抽象的过程中,发现系统中的对象(类),及对象(类)之间的关系 // 以对象(类)及关系(类关系)为基础,为每一个对象(类)设计属性与方法 // 类与对象的区别 // 类是对象的抽象描述,如停车场中的每一辆车是一个具体的对象,统称为汽车类 // 在座的每一个学生是一个具体的对象,统称为学生类 // 以教务系统为例,进行面向对象分析 // 系统场景分析:学生会选课、查看课表、查看课程成绩;教师会录入课程成绩、教授课程 // 系统中三个基本的对象:学生、教师、课程 class Student; class Professor; class Course; //... //继续为每一个类设计属性与基本方法(和自身有关) class Student { // 学生所具有的属性 string Name; string ID; string BirthDate; string Gender; string Address; string Contact; list<Course> Courses; list<string> Awards; list<string> Punishment; //... // 学生能够实现的功能 Student(); ~Student(); // 打印成绩单 Course* PrintCourseReport(); // 打印在读证明 void PrintEnrollingCertificate(); // 添加获奖记录 void AddAword(); //... }; // 继续设计不同类之间的关系,学生与教师的关系,学生与课程关系 // 在设计不同类之间的关系时,可以设定访问权限 // 权限符号包括: // public:后续内容全部公开,任何其他类都可以访问 // protect:后续内容,根据其他类与本类的关系选择性公开 // private:后续内容对其他任何类都不公开 class Car { public: string Brand; string Price; void Move(); void Stop(); protected: string MotoID; // 司机和维修人员可以查看发动机ID void OpenCarFrontCover(); // 司机和维修人员可以打开,其他人不可以 private: string DiverInfo; // 只有司机可以查看 void DestroyCar(); }; // 重新设计,带有访问权限的学生类 class Student { private: string _name; string _id; string _birthDate; string _gender; string _address; string _contact; list<string> _awards; list<string> _punishment; list<Course> _courses; //... public: // 可以通过Getter和Setter方法,为每一个属性设定读写权限 //Getter/Setters string GetName(){ return _name }; // 姓名,只读 void SetContact(string value) { _contact = value;} // 联系方式,可读写 string GetContact() { return _contact; } //... Student(); ~Student(); private: // 打印成绩单和在读证明的功能只有学生自己使用 Course* PrintCourseReport(); void PrintEnrollingCertificate(); public: // 班主任也可以访问 void AddAword(); void AddCourse(Course*); void IsEnrolledCourse(Course*); //... };
4. 通过重载与继承实现多态化
// 考虑教师和学生类 class Student { public: string Name; string Age; string Gender; string Group; // 班级 }; class Professor { public: string Name; string Age; string Gender; string Level; // 职称 }; // 可以看到学生与老师既有相同的属性,也有不同的差异属性 // 姓名、年龄、性别是对于任何人都具有的特点,而班级只有学生才有,职称只有老师才有 // 在进行数据抽象时,可以继续进行抽象,设置基类(Base Class) class Staff // Base Class 基类(父类) { public: string Name; string Age; string Gender; string ID; }; // Child Class 子类 class Student : public Staff { public: string Group; }; class Professor : public Staff { public: string Level; }; // 通过继承类的方式,可以让基类在子类中表现出更多的属性 // 除了在子类中扩展属性,也可以在子类中扩展方法 // 通过子类来扩展基类的这种行为,称为面向对象程序的多态性 // 在子类中扩展方法的方式有两种 // 1. 添加新的函数 // 2. 对父类中已存在的函数进行重写,称为重载 class Staff { public: void Pay() // 在校的职工都具有接收资金的功能 { // 但是不同人员支付的方式不一样,因此在父类中,可以写一个空函数 } }; class Student: public Staff { public: void PayScholarship(); // 子类扩展新的方法,学生还可以收到奖学金 // 与父类同名不同参数列表的函数,称为重载函数 // 父类中的Pay()函数与这个函数共存 void Pay(int salaryPerMonth); // 与父类中的函数返回值、参数列表、名称完全一样,称为重定义函数 // 父类中的Pay()函数将被隐藏 void Pay() { // 学生每月定期发补助 } // 除了重载方法,还可以重载符号 // 格式为:返回值 operator 符号 (参数列表) int operator +(); Student operator [](); }; // 基类中有时候只提供需要实现的函数列表,同时在子类中对这些函数进行实现 // 基类中的这些函数被称为虚函数,使用virtual与override关键字实现 class Staff { public: virtual void Pay(); }; class Student: public Staff { public: // 重载函数,父类中的函数被隐藏 void Pay() override { } };
#include <iostream> using namespace std; namespace Calculate { class Print { }; } namespace Time { class Print { }; } namespace Encoding { class Print { }; } using namespace Encoding; int main() { Calculate::Print cp; Time::Print tp; Print ep; // Encoding::Print return 0; }
5. 通过虚函数与接口实现抽象化
//传统的面向对象程序设计(C++),会出多重继承 //一个子类可能具有多个父类,这种继承方式很容易造成混乱 //现代面向对象程序设计,通常使用的是单继承类,多继承接口的方式 //接口是只有方法声明,而没有定义的类,接口也不具有属性 //C++中的接口使用虚类来实现,其他高级语言有专用关键字Interface // 多重继承 class A {}; class B {}; class C {}; class D: public A {}; class E: public D, B {}; class F: public D, B, A{}; // 错误,D已经继承A,多重继承的混乱 // C++中的接口,直一个类中,所有的函数都是虚函数或纯虚函数,纯虚函数就是子类必须实现 // 对于接口,约定不可以有任何属性,并且使用Interface作为类名前缀 // 其他高级语言如C#,JAVA,Python等,都有专门的接口声明关键字interface class InterfaceA { virtual void FuncA() = 0; // 纯虚函数,子类必须实现 virtual void FuncB(); // 虚函数,子类选择性实现 }; // 对于接口设计方式,允许多级继承,但是约定,不可以多重继承类 class G: A, InterfaceA {}; class H: G, InterfaceA // 接口会在H类中再次被重写,继承关系为单链,H→G→A {}; // 有了接口的定义,在进行面向对象设计时,除了对数据进行抽象化,还可以对方法进行抽象化 // 分别对应类设计和接口设计 class Staff { public: string ID; string Name; string Gender; }; class PaymentInterface { public: virtual void Payment() = 0; }; //学生不需要发工资,所以不继承接口 class Student : public Staff { }; class Professor : public Staff, public PaymentInterface { public: void Payment() override; // 重载函数,父类中Payment()函数被隐藏 void Payment(int award, int payment, int project); // 重载子类Payment()函数,与子类的Payment()函数共存 };
6. 值类型与引用类型
#include <iostream> using namespace std; // C++ 中的指针,引用,与值 class A { public: int Value; }; void func1(A a, int b) { a.Value = 10; b = 10; } void func2(A& a, int& b) { a.Value = 20; b = 20; } void func3(A* a, int* b) { a->Value = 30; *b = 30; } int main() { // C++ 中的值类型、引用类型与指针类型 A a; a.Value = 5; int b = 5; cout << "a = " << a.Value << " ; b = " << b << endl; // 值类型在传递时,会开辟一块新的内存空间,并将数据复制过去 // 参数中,新的变量与旧的变量表示不同的内存数据 func1(a, b); cout << "a = " << a.Value << " ; b = " << b << endl; // 引用类型在传递时,不会开辟新的空间 // 参数中,新的变量与旧的变量指向同一地址 func2(a, b); cout << "a = " << a.Value << " ; b = " << b << endl; // 指针类型在传递时,不会开辟新的空间 // 参数中,指针指向旧的变量所在的内存地址 func3(&a, &b); cout << "a = " << a.Value << " ; b = " << b << endl; return 0; // 在C++中,引用和指针的意义相似,但是指针的灵活性要比引用高出很多 } // 现代的纯面向对象语言中,不在具有指针和引用两个操作 // 如,C#,JAVA,Python,Swift等 // 系统会默认根据变量的类型,来选择参数传递时是使用值传递还是引用传递 // 默认情况下,所有的数值类型:int,float,double,bool,char,struct,enum以值传递的方式传递 // 其他所有类型,都以引用的方式进行传递 // 在现代的高级语言中,可以将值类型封装为引用类型,也可以将引用类型拆解为值类型 // 这两个操作被称为:数据的装箱和拆箱
7. 其他
#include <iostream> using namespace std; class SampleClass { public: static int StaticValue; int VariableValue; static void Func() {} void Func1() {} }; int SampleClass::StaticValue = 0; // 静态变量初始化,必须 int main() { SampleClass a, b; a.StaticValue = 10; a.VariableValue = 10; b.StaticValue = 20; b.VariableValue = 20; SampleClass::StaticValue = 30; SampleClass::Func(); // SampleClass::Func1(); a.Func1(); cout << "a.StaticValue = " << a.StaticValue << " ; a.VariableValue = " << a.VariableValue << endl; cout << "b.StaticValue = " << b.StaticValue << " ; b.VariableValue = " << b.VariableValue << endl; return 0; }
#include <iostream> using namespace std; // 构造函数、析构函数、拷贝构造函数 class Complex { // z = a + ib int a; int b; // C++ 默认提供三个函数 // 默认构造函数 //Complex() {} // 默认拷贝构造函数 /* Complex(const Complex &value) { // 拷贝所有属性的值 } */ // 默认析构函数 //~Complex() {}; Complex() { cout << "Constructor" << endl; } Complex(const Complex &value) { cout << "Deep Copy Constructor" << endl; } ~Complex() { cout << "Destructor" << endl; } };
#include <iostream> using namespace std; // 类型兼容 class Root{ public: int RootValue; }; class Child: public Root { int ChildValue; }; int main() { Root root; Child child; Root* ptrRoot; Child* ptrchild; root = (Root)child; // 此时,ptrchild->ChildValue有值,但无法被访问 child = root; // error child = (Child)root; // error ptrRoot = &root; ptrRoot = &child; // 此时,ptrchild->ChildValue有值,但无法被访问 ptrchild = &child; ptrchild = &root; // error ptrchild = (Child* )&root; // 此时,ptrchild->ChildValue为随机值 return 0; }
// 类只有被实例化(变量)后,才具有内存空间并被赋值 // 但如果在设计类时,就需要访问类属性时,可以使用this指针 // 此外,this指针还可以用来区分同名的变量 class A { int a; int b; int AddAB; A(int a, int b) { this->a = a; this->b = b; this->AddAB = this->a + this->b; } };
8. 作业
实现扫雷游戏,如下图
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具