尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

C++进阶教程:继承与多态

C++进阶教程:继承与多态 本文是 C 系列教程的第 8 篇。上一篇讲解了运算符重载本篇讲解面向对象的核心继承三种方式、构造析构顺序、与多态虚函数、动态绑定、纯虚函数与抽象类、虚析构、override/final、RTTI。一、继承基础1.1 为什么需要继承继承让新类派生类复用已有类基类的成员并可以扩展新功能实现代码复用和层次抽象。例如猫和狗都是动物可以继承 Animal 的共性再各自扩展。1.2 继承的基本语法#includeiostream#includestringusingnamespacestd;// 基类父类classAnimal{protected:// 派生类可访问外部不可访问string name;public:Animal(string n):name(n){}voideat()const{coutname 在吃东西endl;}stringgetName()const{returnname;}};// 派生类子类public 继承classDog:publicAnimal{public:Dog(string n):Animal(n){}// 调用基类构造voidbark()const{coutname 汪汪叫endl;}};classCat:publicAnimal{public:Cat(string n):Animal(n){}voidmeow()const{coutname 喵喵叫endl;}};intmain(){Dogdog(旺财);Catcat(咪咪);dog.eat();// 继承的方法dog.bark();// 自己的方法cat.eat();cat.meow();return0;}1.3 三种继承方式classBase{public:intpub;protected:intprot;private:intpriv;};// public 继承public→publicprotected→protectedclassPubChild:publicBase{};// protected 继承public→protectedprotected→protectedclassProtChild:protectedBase{};// private 继承public→privateprotected→privateclassPrivChild:privateBase{};继方式基类 public 成员基类 protected 成员public 继承仍为 public仍为 protectedprotected 继承降为 protected仍为 protectedprivate 继承降为 private降为 private实际开发中几乎只用 public 继承它表达「is-a」关系。二、派生类的构造与析构#includeiostreamusingnamespacestd;classBase{private:intvalue;public:Base(in t v):value(v){coutBase 构造: valueendl;}~Base(){coutBase 析构endl;}};classDerived:publicBase{private:intextra;public:// 派生类构造必须先初始化基类部分Derived(intv,inte):Base(v),extra(e){coutDerived 构造: extraextraendl;}~Derived(){coutDerived 析构endl;}};intmain(){cout创建派生对象:endl;Derivedd(10,20);cout销毁派生对象:endl;return0;}输出顺序创建派生对象: Base 构造: 10 Derived 构造: extra20 销毁派生对象: Derived 析构 Base 析构规则构造顺序 基类 → 成员 → 自身析构顺序相反。派生类构造必须显式或隐式调用基类构造函数。三、多态与虚函数3.1 什么是多态多态Polymorphism指同一个调用在不同对象上表现出不同行为。通过基类指针/引用调用虚函数实际执行的是派生类的实现。多态三要素基类中定义虚函数。派生类重写虚函数。通过基类指针/引用调用。3.2 虚函数与动态绑定#includeiostreamusingnamespacestd;classShape{public:// 虚函数允许派生类重写支持多态virtualdoublearea()const{return0;}virtualvoidwhoAmI()const{cout我是图形endl;}};classCircle:publicShape{private:doubleradius;public:Circle(doubler):radius(r){}// 重写虚函数override 是 C11 建议标注doublearea()constoverride{return3.14159*radius*radius;}voidwhoAmI()constoverride{cout我是圆endl;}};classRectangle:publicShape{private:doublew,h;public:Rectangle(doublea,doubleb):w(a),h(b){}doublearea()constoverride{returnw*h;}voidwhoAmI()constoverride{cout我是矩形endl;}};intmain(){// 基类指针指向派生类对象Shape*s1newCircle(3);Shape*s2newRectangle(4,5);// 同一个调用不同行为动态绑定s1-whoAmI();// 我是圆s2-whoAmI();// 我是矩形cout面积: s1-area()endl;// 28.27cout面积: s2-area()endl;// 20deletes1;deletes2;return0;}3.3 静态绑定 vs 动态绑定维度静态绑定动态绑定时机编译期决定运行期决定依据指针/引用静态类型对象实际类型函数非虚函数虚函数性能更快有虚函数表查找开销3.4 虚函数表vtable含有虚函数的类会生成虚函数表vtable对象头部有虚指针vptr指向它。调用虚函数时通过 vptr → vtable → 实际函数地址实现动态绑定。Shape 对象: [vptr] - [Shape::area, Shape::whoAmI] Circle 对象: [vptr] - [Circle::area, Circle::whoAmI] 重写后四、纯虚函数与抽象类4.1 抽象类含纯虚函数的类称为抽象类不能实例化只能作为基类被继承#includeiostreamusingnamespacestd;// 抽象类含纯虚函数classShape{public:virtualdoublearea()const0;// 纯虚函数 0virtualvoiddraw()const0;// 抽象类也可以有普通成员函数voidshow()const{cout面积: area()endl;}// 抽象类需要虚析构见第五节virtual~Shape(){}};classCircle:publicShape{private:doubleradius;public:Circle(doubler):radius(r){}// 必须实现所有纯虚函数才能实例化doublearea()constoverride{return3.14159*radius*radius;}voiddraw()constoverride{cout画一个圆endl;}};intmain(){// Shape s; // 错误抽象类不能实例化Shape*shapenewCircle(3);shape-draw();// 画一个圆shape-show();// 面积: 28.2743deleteshape;return0;}4.2 抽象类的用途定义接口规范强制派生类实现。用于设计模式工厂、策略等。表达「是一个」的抽象层次。五、虚析构函数#includeiostreamusingnamespacestd;classBase{public:// 非虚析构问题版本// ~Base() { cout Base 析构 endl; }// 虚析构正确通过基类指针删除时正确调用派生类析构virtual~Base(){coutBase 析构endl;}};cl ass Derived:publicBase{private:int*data;public:Derived():data(newint[100]){coutDerived 构造: 分配内存endl;}~Derived(){delete[]data;// 释放内存coutDerived 析构: 释放内存endl;}};intmain(){Base*pnewDerived();deletep;// 若 Base 析构非虚这里不会调用 Derived 析构 → 内存泄漏return0;}关键凡是可能被继承且通过基类指针删除的类析构函数必须声明为 virtual。六、override 与 final6.1 override显式重写classBase{public:virtualvoidfunc(){}};classDerived:publicBase{public:// override 标记编译器检查确实重写了基类虚函数// 若拼写错误如 funcc编译器报错voidfunc()override{}};6.2 final禁止继承/重写#includeiostreamusingnamespacestd;classBase{public:virtualvoidshow(){coutBaseendl;}};// final 类禁止被继承classFinalClassfinal:publicBase{public:// final 虚函数禁止子类重写voidshow()overridefinal{coutFinalClassendl;}};// class Child : public FinalClass {}; // 错误FinalClass 是 finalintmain(){FinalClass fc;fc.show();// FinalClassreturn0;}七、RTTIdynamic_cast 与 typeid7.1 dynamic_cast安全向下转型#includeiostream#includetypeinfousingnamespacestd;classAnimal{public:virtual~Animal(){}// 必须有虚函数才能 dynamic_cast};classDog:publicAnimal{public:voidbark(){cout汪汪endl;}};classCat:publicAnimal{public:voidmeow(){cout喵喵endl;}};intmain(){Animal*a1newDog();Animal*a2newCat();// dynamic_cast安全的向下转型失败返回 nullptrDog*ddynamic_castDog*(a1);if(d){d-bark();// 汪汪}Dog*d2dynamic_castDog*(a2);// a2 实际是 Catif(d2){d2-bark();// 不会执行}else{couta2 不是 Dog 类型转型失败endl;}deletea1;deletea2;return0;}7.2 typeid获取类型信息#includeiostream#includetypeinfousingnamespacestd;classAnimal{public:virtual~Animal(){}};classDog:publicAnimal{};classCat:publicAnimal{};intmain(){Animal*a1newDog();Animal*a2newCat();couta1 实际类型: typeid(*a1).name()endl;// class Dogcouta2 实际类型: typeid(*a2).name()endl;// class Catif(typeid(*a1)typeid(Dog)){couta1 是 Dogendl;}deletea1;deletea2;return0;}八、实战图形系统综合本篇全部知识实现完整图形系统#includeiostream#includevectorusingnamespacestd;// 抽象基类classShape{protected:string name;public:Shape(string n):name(n){}virtualdoublearea()const0;// 纯虚函数virtualvoidinfo()const0;virtual~Shape(){}// 虚析构};classCircle:publicShape{private:doubleradius;public:Circle(string n,doubler):Shape(n),radius(r){}doublearea()constoverride{return3.14159*radius*radius;}voidinfo()constoverride{coutname圆半径 radius面积: area()endl;}};classRectangle:publicShape{private:doublew,h;public:Rectangle(string n,doublea,doubleb):Shape(n),w(a),h(b){}doublearea()constoverride{returnw*h;}voidinfo()constoverride{coutname矩形 wxh面积: area()endl;}};classTriangle:publicShape{private:doublebase,height;public:Triangle(string n,doubleb,doublehh):Shape(n),base(b),height(hh){}doublearea()constoverride{return0.5*base*height;}voidinfo()constoverride{coutname三角形面积: area()endl;}};intmain(){// 多态容器基类指针数组管理多种派生对象vectorShape*shapes;shapes.push_back(newCircle(圆形,3));shapes.push_back(newRectangle(矩形,4,5));shapes.push_back(newTriangle(三角形,6,4));doubletotal0;for(Shape*s:shapes){s-info();totals-area();}cout总面积: totalendl;// 释放内存虚析构保证正确for(Shape*s:shapes){deletes;}return0;}运行示例圆形圆半径 3面积: 28.2743 矩形矩形 4x5面积: 20 三角形三角形面积: 12 总面积: 60.2743总结本篇讲解了继承的三种方式与构造析构顺序、多态三要素、虚函数与虚函数表、纯虚函数与抽象类、虚析构函数的重要性、override/final、RTTIdynamic_cast/typeid并用图形系统串联实战。重点掌握public 继承的 is-a 语义、多态的实现条件、抽象类不能实例化、基类析构必须 virtual。下一篇将讲解动态内存管理与智能指针RAII、unique_ptr/shared_ptr/weak_ptr敬请期待
返回列表