网站建设 福步什么是电商直播
2026/4/17 0:39:30 网站建设 项目流程
网站建设 福步,什么是电商直播,宁夏公路建设管理局网站,西地那非片的正确服用方法与效果目录8.1 定义8.2 继承的实现8.3 继承类型私有继承公有继承保护继承8.4 菱形问题与虚拟继承8.5 实例展示8.5.1 实现继承错误案例解决第一处错误解决第二处错误8.5.2 虚函数8.5.3 纯虚函数8.5.4 继承的缺点组合8.1 定义 继承#xff1a;一个类从另一个类继承属性的机…目录8.1 定义8.2 继承的实现8.3 继承类型私有继承公有继承保护继承8.4 菱形问题与虚拟继承8.5 实例展示8.5.1 实现继承错误案例解决第一处错误解决第二处错误8.5.2 虚函数8.5.3 纯虚函数8.5.4 继承的缺点组合8.1 定义继承一个类从另一个类继承属性的机制。直观实例Is-A 关系std::ifstream 是 std::istream而 std::istream 是 std::ios.动态多态性不同类型的对象可能需要相同的接口可扩展性继承允许通过创建具有特定属性的子类来扩展类8.2 继承的实现以上几何体都有几何领域维度描述术语比如表面积半径宽度体积….h文件classShape{public:// ​纯虚函数在基类中声明无法实例化只能在子类中重写virtualdoublearea()const0;};// 声明Circle类它继承于Shape类classCircle:publicShape{public:// 列表初始化构造函数Circle(doubleradius):_radius{radius}{};// 为Circle类重写基类函数area()doublearea()const{return3.14*_radius*_radius;}private:// 继承的另一个优点是类变量的封装double_radius;};classRectangle:publicShape{public:// constructorRectangle(doubleheight,doublewidth):_height{height},_width{width}{};doublearea()const{return_width*_height;}private:double_width,_height;};8.3 继承类型访问权限类型Typepublic公有继承protected保护继承private私有继承默认示例Exampleclass B: public A {…}class B: protected A {…}class B: private A {…}公有成员Public Members在派生类中仍为公有public在派生类中变为保护protected在派生类中变为私有private保护成员Protected Members在派生类中仍为保护protected在派生类中仍为保护protected在派生类中变为私有private私有成员Private Members在派生类中不可访问在派生类中不可访问在派生类中不可访问私有继承公有继承公有继承能更好地模拟“是一个”关系玩家确实是一个实体因为它公开地展示了实体的所有功能。// 默认私有继承classEntity{public:booloverlapsWith(constEntityother);};classPlayer:/* private */Entity{// Private inheritance:// - private members of Entity are inaccessible to all// - public members become private (inaccessible to outside)};// 改成公有继承classEntity{public:booloverlapsWith(constEntityother);};classPlayer:publicEntity{// Public inheritance:// - private members of Entity are still inaccessible// - public members become public (accessible to outside)};保护继承受保护的成员对子类可见但对外部不可见classEntity{protected:doublex,y,z;HitBox hitbox;public:voidupdate();voidrender();};classBase{public:intpublicVar;protected:intprotectedVar;private:intprivateVar;};classDerived:protectedBase{// protected 继承public:voidaccessBaseMembers(){publicVar10;// OK: Base::publicVar 在 Derived 中变为 protectedprotectedVar20;// OK: Base::protectedVar 仍是 protected// privateVar 30; // 错误: Base::privateVar 不可访问}};intmain(){Derived d;// d.publicVar 10; // 错误: Base::publicVar 在 Derived 外是 protected无法访问// d.protectedVar 20; // 错误: protected 成员无法在类外访问return0;}8.4 菱形问题与虚拟继承“The Diamond Problem”​菱形问题​是面向对象编程中多重继承的典型问题。当类 A 同时继承类 B 和类 C而类 B 和类 C 又均继承自类 D 时类 A 会包含类 D 的两个副本可能导致数据冗余或调用歧义其继承结构形似菱形故得名。例如解决此问题的方法是让 “员工”Employee类和 “学生”Student类以虚拟继承的方式从 “人”Person类继承。虚拟继承是指一个派生类在此处为 “部门负责人”SectionLeader类应当仅包含其基类在此处为 “人”Person类的单个实例。classStudent:publicvirtualPerson{protected:std::string idNumber;std::string advisor;std::string major;uint16_tyear;public:std::stringgetIdNumber()const;Student(conststd::stringname,…);std::stringgetMajor()const;uint16_tgetYear()const;voidsetYear(uint16_tyear);voidsetMajor(conststd::stringmajor);std::stringgetAdvisor()const;voidsetAdvisor(conststd::stringadvisor);This slide is hidden};classEmployee:publicvirtualPerson{protected:doublesalary;public:virtualstd::stringgetRole()const0;Employee(conststd::stringname);virtualdoublegetSalary()const0;virtualvoidsetSalary()const0;virtual~Employee()default;};这要求派生类对基类进行初始化8.5 实例展示8.5.1 实现继承很多冗余且不方便修改想象一下我们想要给每个对象添加一个 overlapsWith 方法该方法用于检查它是否与另一个对象在空间上重叠。它们有共同的性质引入基类Entity现在继承了还有冗余继续继承继承树定义了 “是一个” 的关系。定义通用功能overlapsWith很简单我们将通过为每种实体重写更新和渲染方法来实现游戏的逻辑。让我们为每种实体类型重写更新和渲染函数吧游戏本质上是一系列实体的集合每帧都会对这些实体进行更新和渲染错误案例我们试一下#includeiostream#includevectorclassEntity{protected:doublex,y,z;HitBox hitbox;public:virtualvoidupdate(){}virtualvoidrender(){};};classPlayer:publicEntity{doublehitpoints100;public:voiddamage(doublehp){hitpoints-hp;}voidupdate()override{std::coutUpdating Player!std::endl;}voidrender()override{std::coutRendering Player!std::endl;}};classTree:publicEntity{public:voidupdate()override{std::coutUpdating Tree!std::endl;}voidrender()override{std::coutRendering Tree!std::endl;}};classProjectile:publicEntity{private:doublevx,vy,vz;public:voidupdate()override{std::coutUpdating Projectile!std::endl;}voidrender()override{std::coutRendering Projectile!std::endl;}};intmain(){Player player;Tree tree;Projectile proj;std::vectorEntityentities{player,tree,proj};while(true){std::coutRendering frame...std::endl;for(autoentity:entities){entity.update();entity.render();}}std::vectorEntity*entities{player,tree,proj};while(true){std::coutRendering frame...std::endl;for(autoentity:entities){entity-update();entity-render();}}return0;}解决第一处错误回想一下C 会按顺序排列对象的字段。C 会将子类的成员存放在继承的成员下方注意当你将派生类赋值给基类时会发生切片现象向量中的每个元素都是一个 Entity因此编译器会调用Entity::update ()该函数不执行任何操作而不是Player::update ()、Tree::update ()、Projectile::update ()等。解决方案用​Entity*intmain(){Player player;Tree tree;Projectile proj;std::vectorEntity*entities{player,tree,proj};while(true){std::coutRendering frame...std::endl;for(autoentity:entities){entity-update();entity-render();}}return0;}指针通过避免复制来保留子类的细节解决第二处错误问题调用哪一个呢给定一个指向实体Entity的指针编译器是如何知道该调用哪个方法的呢我们应该调用与实体所指向的对象类型相匹配的更新方法。但仅仅一个实体Entity*并不能告诉我们任何关于其类型的信息编译器默认假设 entity 指向一个 Entity。因为Entity是它唯一能绝对确定任何实体都会支持的类。注意对象的编译时类型和运行时类型之间存在差异在编译时它被视为一个实体。在运行时它可以是一个实体或任何子类例如投射物、玩家等。我们需要的是​动态分派​——根据对象的运行时动态类型应该调用分派不同的方法引入虚函数#includeiostream#includevectorclassEntity{protected:doublex,y,z;HitBox hitbox;public:// 加 virtualvirtualvoidupdate(){}virtualvoidrender(){};};classPlayer:publicEntity{doublehitpoints100;public:voiddamage(doublehp){hitpoints-hp;}// 加 overridevoidupdate()override{std::coutUpdating Player!std::endl;}voidrender()override{std::coutRendering Player!std::endl;}};classTree:publicEntity{public:voidupdate()override{std::coutUpdating Tree!std::endl;}voidrender()override{std::coutRendering Tree!std::endl;}};classProjectile:publicEntity{private:doublevx,vy,vz;public:voidupdate()override{std::coutUpdating Projectile!std::endl;}voidrender()override{std::coutRendering Projectile!std::endl;}};intmain(){Player player;Tree tree;Projectile proj;std::vectorEntity*entities{player,tree,proj};while(true){std::coutRendering frame...std::endl;for(autoentity:entities){entity-update();entity-render();}}return0;}8.5.2 虚函数将函数标记为虚函数可启用动态分派子类可以重写此方法override 并非必需但有助于提高可读性它会检查你是否在重写一个虚方法而非创建一个新方法。在函数前添加 virtual会给每个对象添加一些元数据。具体来说它会添加一个指向虚函数表称为 vtable的指针称为 vpointer该虚函数表说明了对于每个虚方法应为该对象调用哪个函数。Python 会在其内存占用中存储有关对象类型的额外信息这使得运行时类型检查成为可能。virtual 有点像 Python。Python 和 C 的虚函数都存储特定于类型的信息:在许多其他语言中类函数默认是虚函数。而在 C 中你必须主动选择使用虚函数因为它们的代价更高。这会增加类的内存布局大小。查找虚函数表vtable和调用方法会花费更长时间。在量化金融以及那些纳秒级时间都很重要的行业中是不使用虚函数的8.5.3 纯虚函数包含一个或多个纯虚函数的类是抽象类它无法被实例化重写所有纯虚函数会使该类成为具体类classEntity{public:virtualvoidupdate()0;virtualvoidrender()0;};Entity e;// 错误Entity是抽象类Entity is abstract!classProjectile:publicEntity{public:voidupdate()override{};voidrender()override{};};Projectile p;// 正确Projectile是具体类Projectile is concrete当没有明确的默认实现时纯虚函数会很有用classShape{public:virtualdoublevolume()0;};一个 Shape形状的默认体积是多少我们把它标记为纯虚函数让子类来决定吧8.5.4 继承的缺点组合庞大的继承树往往速度更慢且更难理解在电子游戏中为每种不同的对象类型创建子类的方法在现代游戏引擎中并不常见组合通常更灵活而且也更合理继承是is-a关系组合是has关系继承是一种强大的工具但有时组合才更有意义A car​ is ​has​ an engineclassCar{Engine*engine;SteeringWheel*wheel;Brakes*brakes;};classEngine{};classCombustionEngine:publicEngine{};classGasEngine:publicCombustionEngine{};classDieselEngine:publicCombustionEngine{};classElectricEngine:publicEngine{};一种使用技巧指向实现的指针pImplPointer to implementation

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询