类和对象(动物旅馆实例)
一、类 和 对象1类(class)一张「模板 / 图纸」只规定有什么东西、能做什么事不是实实在在的物品。举例子Room房间类 房间图纸图纸上写着每个房间都要有房间号、密码、能入住、能退房。Animal动物类 动物模板模板规定每个动物都有名字、体型都要判断能不能住房间、会叫。图纸本身不能住人、不能养动物它只是规则范本。2对象Object按照模板造出来的真实东西。是看得见、用得上的具体个体。对应代码里1按照Room图纸造出来C01 房间、S01 房间 → 这就是房间对象2按照Animal模板造出来小猴子、小麻雀、小蛇 → 这就是动物对象简单记类 图纸对象 实物。先有图纸再做实物。写一个最简单的类#include iostream using namespace std; // 类房间模板 class Room { // 空的 }; int main() { // 对象造一个房间 Room r1; return 0; }二、访问权限private /public封装相关1private私有的家里的卧室外人不让进、不让乱碰。代码里用法把数据变量都放在private里比如房间号、密码、是否空闲。1外部代码不能直接修改这些数据2比如别人不能强行把 “已入住” 改成 “空闲”也不能乱改房间密码2public公有的家里的客厅所有人都可以正常使用。代码里用法把功能函数 / 动作放在public里比如入住、退房、查房间号。外人想使用房间只能走这些公开的 正规通道3封装整体概念把数据藏起来只留正规操作入口保护内部数据不乱改。结合旅馆举例房间的密码、入住状态是隐私藏进 private想入住、退房只能调用公开的checkIn,checkOut函数public。好处不会出现逻辑混乱比如不会出现 人退房了但密码没改这种 bug。给房间加属性和功能class Room { private: // 隐私房间号、状态外部不能乱改 string roomId; bool isFree; public: // 公开功能设置房间号 void setRoomId(string id) { roomId id; isFree true; } // 查看房间状态 void showStatus() { cout roomId isFree endl; } };三、继承子类继承父类的东西不用自己重新造一遍。子类天生拥有父类有的东西和本事还能再加自己独有的能力。1父类 Room通用房间所有房间都有的能力有房间号、有密码、能入住、能退房、能维修。2子类 CuboidRoom长方体房间、SphereRoom球形房间它们继承了 Room 的所有功能不用再重复写 入住、退房代码。只需要额外加上自己独有的东西1长方体房间多了长、宽、高2球形房间多了半径再看动物父类Animal规定所有动物都有名字、体型、会判断能不能住、会叫。子类Mammal(哺乳类)、Bird(鸟类)直接继承这些基础内容只写自己专属规则就行。简单记继承 抄现成的少写重复代码。继承的使用// 父类 class Room { public: void show() { cout 我是房间; } }; // 子类继承 Room class CuboidRoom : public Room { // 自动拥有 show() 函数 }; int main() { CuboidRoom r; r.show(); // 直接用爸爸的功能 }(表格借用ai)注意1无论哪种继承父类的private成员都会被继承到子类对象里只是子类的成员函数不能直接访问只能通过父类的 public/protected 成员函数间接访问。2例子用public继承是最常见的一种它的作用就是让子类直接复用父类的 public 接口。完整例子#include iostream using namespace std; class Room { public: void show() { cout 我是房间; } int public_w 10; protected: int protected_h 20; private: int private_area 30; //子类不能直接访问 }; class CuboidRoom : public Room { public: void test() { // 可以访问父类的public和protected成员 cout public_w endl; //可以 cout protected_h endl; //可以 // cout private_area endl; //编译报错父类private成员不能直接访问 } }; int main() { CuboidRoom r; r.show(); //可以父类public成员public继承后还是public cout r.public_w endl; //可以 // cout r.protected_h endl; //外部不能访问protected成员 // cout r.private_area endl; //完全不可见 return 0; }使用接口例子#include iostream using namespace std; class Room { // // 1. 私有成员只有自己能用 // private: string password; //房间密码私有 // // 2. 保护成员自己 儿子能用 // protected: bool isFree; //是否空闲保护 // // 3. 公开接口外面都能调用 // public: //设置密码修改 private void setPassword(string pwd) { password pwd; } //获取密码访问 private string getPassword() { return password; } //设置房间状态修改 protected void setFree(bool status) { isFree status; } //获取房间状态访问 protected bool getFree() { return isFree; } }; int main() { Room r; // // 外面不能直接访问 // r.password 123; 报错 // r.isFree true; 报错 // // // 必须通过 public 接口访问/修改 // r.setPassword(666888); r.setFree(true); cout 密码 r.getPassword() endl; cout 是否空闲 r.getFree() endl; return 0; }四、虚函数 纯虚函数 抽象类1虚函数 virtual父类定了一个 通用动作子类可以改成自己的玩法。举例父类 Room说所有房间都要告诉我你是什么类型。有的子类长方体房间回答我是长方体有的子类球形房间回答我是球形。同一个动作 “报类型”不同房间做法不一样就用“virtual”标记。2纯虚函数 virtual ... 0父类只提要求自己完全不实现逼着每个子类必须自己做。格式长这样: virtual 函数() 0; 举例父类Animal动物模板规定所有动物必须自己判断能不能住房间、必须会叫。但是父类自己不写具体怎么做直接甩给子类 这个功能我不管你们每个动物必须自己写规则3抽象类只提规矩、完全不能直接拿来用的 纯模板。只要一个类里有纯虚函数它就是抽象类。1抽象类不能造出实物对象比如不能直接造出一个 普通动物、普通房间2只能用它的子类造对象猴子、麻雀、长方体房间……结合代码理解Animal、Room都是抽象类只定规矩真正能用的是它们的子类哺乳动物、鸟类、球形房间等。相关代码#include iostream using namespace std; // // 【抽象类】 // 因为里面有 【纯虚函数】所以这个类不能创建对象 // class Room { public: // 1.【虚函数】父类写了实现子类可以重写也可以不重写 virtual void showInfo() { cout 我是一个房间 endl; } // 2.【纯虚函数】父类只定规则不写实现 //子类 必须 重写这个函数 virtual string getType() 0; //虚析构必须写防止内存泄漏 virtual ~Room() {} }; // // 子类1长方体房间 // 必须实现父类的纯虚函数 getType() // class CuboidRoom : public Room { public: // 重写 纯虚函数 string getType() override { return 长方体房间; } // 重写 虚函数 void showInfo() override { cout 我是长方体房间有长宽高 endl; } }; // // 子类2球形房间 // 同样必须实现纯虚函数 // class SphereRoom : public Room { public: string getType() override { return 球形房间; } void showInfo() override { cout 我是球形房间有半径 endl; } }; // // 主函数测试 // int main() { //错误抽象类不能创建对象 // Room room; //正确用子类创建对象 Room* r1 new CuboidRoom(); Room* r2 new SphereRoom(); // 调用重写后的函数 r1-showInfo(); cout 类型 r1-getType() endl; r2-showInfo(); cout 类型 r2-getType() endl; // 释放内存 delete r1; delete r2; return 0; }五多态同一个指令不同的东西做出不同的反应生活例子老师喊请开始表演小学生唱歌中学生跳舞大人演讲指令都是 表演但不同的人做法不一样 → 这就是多态。套进动物旅馆统一指令判断能不能住进这个房间1给哺乳动物猴子执行这个指令规则是可以歪身子把体型长宽高打乱排序再匹配房间2给鸟类麻雀执行这个指令规则是只住球形房间房间半径够大就行3给爬行动物小蛇执行这个指令规则是体型长宽高必须和房间严格对应不能歪身子同一个函数 canCheckIn不同动物执行出不同结果这就是多态。多态的好处旅馆管理员HotelManager”不用区分这是猴子还是麻雀只需要统一发指令去试试能不能住动物自己就会按自己的规则判断管理起来特别省事。多态发生的 3 个条件必须同时满足1 有继承2父类有虚函数3父类指针 / 引用 指向 子类对象多态效果#include iostream using namespace std; // 1. 父类 class Animal { public: // 2. 虚函数 → 多态的关键 virtual void speak() { cout 动物叫 endl; } }; // 子类1狗 class Dog : public Animal { public: void speak() override { cout 汪汪汪 endl; } }; // 子类2猫 class Cat : public Animal { public: void speak() override { cout 喵喵喵 endl; } }; int main() { // 3. 父类指针 指向 不同子类对象 Animal* a1 new Dog(); Animal* a2 new Cat(); // 同一个函数 speak() a1-speak(); // 输出汪汪汪 a2-speak(); // 输出喵喵喵 }六、单例模式PasswordGenerator 密码生成器整个程序里这个东西只能有唯一一个。就像一个小区只有一个物业办公室所有人办业务都找这一个不能再新建第二个物业。对应代码里的密码生成器1密码生成器负责生成 6 位随机密码还要保证密码不重复。2如果有好多个密码生成器就会出现重复密码、管理混乱。3所以用单例: 全局只允许存在一个密码生成器对象。实现的两个小规则1把构造函数设为private不让外人随便新建这个对象。2留一个公开入口getInstance()所有人想使用密码功能都走这一个入口拿到那唯一的一个生成器。简单记单例 全局独一份不能复制。单例模式怎么写4 步固定套路固定 4 步背下来就能写任何单例1. 构造函数私有化 → 不让外面 new2. 拷贝构造、赋值也删掉 → 禁止复制3. 创建一个静态自身指针 → 保存唯一实例4. 提供一个 public 静态接口 getInstance () → 获取唯一实例#include iostream using namespace std; // 单例类密码生成器 class PasswordGenerator { private: // 3. 静态指针保存唯一实例 static PasswordGenerator* instance; // 1. 构造函数私有化重点 PasswordGenerator() { cout 唯一的密码生成器创建成功 endl; } // 2. 禁止拷贝和赋值 PasswordGenerator(const PasswordGenerator) delete; void operator(const PasswordGenerator) delete; public: // 4. 静态接口获取唯一实例 static PasswordGenerator* getInstance() { if (instance nullptr) { instance new PasswordGenerator(); } return instance; } // 测试功能 void generatePassword() { cout 生成密码123ABC endl; } }; // 静态成员必须在类外初始化 PasswordGenerator* PasswordGenerator::instance nullptr; // // 测试 // int main() { // 永远只能用这个方式获取对象 PasswordGenerator* p1 PasswordGenerator::getInstance(); PasswordGenerator* p2 PasswordGenerator::getInstance(); // p1 和 p2 是同一个东西 cout p1 endl; cout p2 endl; p1-generatePassword(); p2-generatePassword(); return 0; }七、构造函数 析构函数1构造函数例子新建一个房间对象时造一个新房间程序会自动运行构造函数默认设置1房间一开始是空闲的2一开始不维修3初始密码为空不用我们手动一步步设置造出来就自带初始状态。2虚析构函数 virtual ~ 类名 ()拆东西的时候保证拆得干干净净不留垃圾。你代码里用了 父类指针指向子类对象的写法加上virtual删除对象时会把父类、子类的内存都正常释放避免程序出问题。日常理解安全拆物件就行。构造函数代码示例#include iostream using namespace std; // // 【抽象类】 // 因为里面有 【纯虚函数】所以这个类不能创建对象 // class Room { public: // 1. 【虚函数】父类写了实现子类可以重写也可以不重写 virtual void showInfo() { cout 我是一个房间 endl; } // 2. 【纯虚函数】父类只定规则不写实现 // 子类 必须 重写这个函数 virtual string getType() 0; // 虚析构必须写防止内存泄漏 virtual ~Room() {} }; // // 子类1长方体房间 // 必须实现父类的纯虚函数 getType() // class CuboidRoom : public Room { public: // 重写 纯虚函数 string getType() override { return 长方体房间; } // 重写 虚函数 void showInfo() override { cout 我是长方体房间有长宽高 endl; } }; // // 子类2球形房间 // 同样必须实现纯虚函数 // class SphereRoom : public Room { public: string getType() override { return 球形房间; } void showInfo() override { cout 我是球形房间有半径 endl; } }; // // 主函数测试 // int main() { // 错误抽象类不能创建对象 // Room room; // 正确用子类创建对象 Room* r1 new CuboidRoom(); Room* r2 new SphereRoom(); // 调用重写后的函数 r1-showInfo(); cout 类型 r1-getType() endl; r2-showInfo(); cout 类型 r2-getType() endl; // 释放内存 delete r1; delete r2; return 0; }八、override 关键字明确告诉别人我这个函数是专门重写父类规定的那个动作。比如父类要求 必须报房间类型子类写string getType() override意思就是我现在写的这个函数就是专门实现父类要求的功能防止写错名字、写错格式。属于”辅助标记让代码更规范。#include iostream using namespace std; // 父类 class Animal { public: // 虚函数 virtual void speak() { cout 动物叫 endl; } }; // 子类 class Dog : public Animal { public: // 明确标记重写父类虚函数 void speak() override { cout 汪汪汪 endl; } }; int main() { Animal* a new Dog(); a-speak(); return 0; }不用 override 会怎样危险class Animal { public: virtual void speak() {} }; class Dog : public Animal { public: // 写错成 speek少个a void speek() { cout 汪汪; } };用了 override 会怎样安全你写错编译器立刻报错帮你发现问题class Dog : public Animal { public: // 加了 override void speek() override { // 直接报错 } };九、整体串一遍结合旅馆从头到尾梳理1先画图纸类房间图纸、动物图纸、密码器图纸2图纸分内外隐私数据藏起来private功能对外公开public→ 封装3通用图纸父类 Room/Animal定下基础功能细分图纸子类直接沿用 → 继承4通用图纸只提要求不实现纯虚函数变成抽象类不能直接用5不同实物猴子、麻雀执行同一个指令做法不同 → 多态6密码器规定全局只能有一个 → 单例7造物件时自动初始化构造函数销毁时安全清理析构函数十、工厂模式1、工厂模式是什么工厂 专门生产对象的地方你不用自己new对象告诉工厂你要什么工厂直接给你造好2、生活例子1你想喝一杯奶茶2你不用自己买茶叶、煮奶、加糖3奶茶工厂店直接做好给你代码里1你想要一个房间对象2你不用自己new CuboidRoom()3房间工厂直接给你造好返回3、工厂模式有什么好处1不用自己 new 对象代码更简单2创建和使用分离方便维护3统一管理对象创建4完美配合 继承 多态4、代码示例#include iostream using namespace std; // // 1. 抽象类定规则 // class Room { public: virtual string getType() 0; virtual ~Room() {} }; // // 2. 具体子类 // class CuboidRoom : public Room { public: string getType() override { return 长方体房间; } }; class SphereRoom : public Room { public: string getType() override { return 球形房间; } }; // // 3. 工厂类专门造房间 // class RoomFactory { public: // 传入类型工厂自动创建对象 static Room* createRoom(string type) { if (type cuboid) { return new CuboidRoom(); } else if (type sphere) { return new SphereRoom(); } return nullptr; } }; // // 4. 使用工厂超级简单 // int main() { // 工厂直接给你造好房间 Room* r1 RoomFactory::createRoom(cuboid); Room* r2 RoomFactory::createRoom(sphere); cout r1-getType() endl; cout r2-getType() endl; delete r1; delete r2; return 0; }结果长方体房间 球形房间5、工厂模式核心逻辑工厂负责创建对象你只管使用不用管怎么创建六、和学过的知识点关系工厂模式 必须用到1. 类和对象2. 继承3. 多态4. 虚函数 / 抽象类七、总结1工厂造对象的地方2你只管拿对象用3好处代码简单、好维护、易扩展