C++进阶教程:继承与多态
2026/8/30 6:58:39 网站建设 项目流程

本文是 C++ 系列教程的第 8 篇。上一篇讲解了运算符重载,本篇讲解面向对象的核心:继承(三种方式、构造析构顺序、与多态(虚函数、动态绑定、纯虚函数与抽象类、虚析构、override/final、RTTI)。

一、继承基础

1.1 为什么需要继承

继承让新类(派生类)复用已有类(基类)的成员,并可以扩展新功能,实现代码复用层次抽象。例如:猫和狗都是动物,可以继承 Animal 的共性,再各自扩展。

1.2 继承的基本语法

#include<iostream>#include<string>usingnamespacestd;// 基类(父类)classAnimal{protected:// 派生类可访问,外部不可访问string name;public:Animal(string n):name(n){}voideat()const{cout<<name<<" 在吃东西"<<endl;}stringgetName()const{returnname;}};// 派生类(子类):public 继承classDog:publicAnimal{public:Dog(string n):Animal(n){}// 调用基类构造voidbark()const{cout<<name<<" 汪汪叫"<<endl;}};classCat:publicAnimal{public:Cat(string n):Animal(n){}voidmeow()const{cout<<name<<" 喵喵叫"<<endl;}};intmain(){Dogdog("旺财");Catcat("咪咪");dog.eat();// 继承的方法dog.bark();// 自己的方法cat.eat();cat.meow();return0;}

1.3 三种继承方式

classBase{public:intpub;protected:intprot;private:intpriv;};// public 继承:public→public,protected→protectedclassPubChild:publicBase{};// protected 继承:public→protected,protected→protectedclassProtChild:protectedBase{};// private 继承:public→private,protected→privateclassPrivChild:privateBase{};
继��方式基类 public 成员基类 protected 成员
public 继承仍为 public仍为 protected
protected 继承降为 protected仍为 protected
private 继承降为 private降为 private

实际开发中几乎只用 public 继承,它表达「is-a」关系。

二、派生类的构造与析构

#include<iostream>usingnamespacestd;classBase{private:intvalue;public:Base(in t v):value(v){cout<<"Base 构造: "<<value<<endl;}~Base(){cout<<"Base 析构"<<endl;}};classDerived:publicBase{private:intextra;public:// 派生类构造:必须先初始化基类部分Derived(intv,inte):Base(v),extra(e){cout<<"Derived 构造: extra="<<extra<<endl;}~Derived(){cout<<"Derived 析构"<<endl;}};intmain(){cout<<"创建派生对象:"<<endl;Derivedd(10,20);cout<<"销毁派生对象:"<<endl;return0;}

输出顺序:

创建派生对象: Base 构造: 10 Derived 构造: extra=20 销毁派生对象: Derived 析构 Base 析构

规则:构造顺序 = 基类 → 成员 → 自身;析构顺序相反。派生类构造必须显式(或隐式)调用基类构造函数。

三、多态与虚函数

3.1 什么是多态

多态(Polymorphism)指同一个调用在不同对象上表现出不同行为。通过基类指针/引用调用虚函数,实际执行的是派生类的实现。

多态三要素

  1. 基类中定义虚函数
  2. 派生类重写虚函数。
  3. 通过基类指针/引用调用。

3.2 虚函数与动态绑定

#include<iostream>usingnamespacestd;classShape{public:// 虚函数:允许派生类重写,支持多态virtualdoublearea()const{return0;}virtualvoidwhoAmI()const{cout<<"我是图形"<<endl;}};classCircle:publicShape{private:doubleradius;public:Circle(doubler):radius(r){}// 重写虚函数(override 是 C++11 建议标注)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*s1=newCircle(3);Shape*s2=newRectangle(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 抽象类

纯虚函数的类称为抽象类,不能实例化,只能作为基类被继承:

#include<iostream>usingnamespacestd;// 抽象类:含纯虚函数classShape{public:virtualdoublearea()const=0;// 纯虚函数:= 0virtualvoiddraw()const=0;// 抽象类也可以有普通成员函数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*shape=newCircle(3);shape->draw();// 画一个圆shape->show();// 面积: 28.2743deleteshape;return0;}

4.2 抽象类的用途

  • 定义接口规范,强制派生类实现。
  • 用于设计模式(工厂、策略等)。
  • 表达「是一个」的抽象层次。

五、虚析构函数

#include<iostream>usingnamespacestd;classBase{public:// 非虚析构(问题版本)// ~Base() { cout << "Base 析构" << endl; }// 虚析构(正确):通过基类指针删除时正确调用派生类析构virtual~Base(){cout<<"Base 析构"<<endl;}};cl ass Derived:publicBase{private:int*data;public:Derived():data(newint[100]){cout<<"Derived 构造: 分配内存"<<endl;}~Derived(){delete[]data;// 释放内存cout<<"Derived 析构: 释放内存"<<endl;}};intmain(){Base*p=newDerived();deletep;// 若 Base 析构非虚,这里不会调用 Derived 析构 → 内存泄漏return0;}

关键:凡是可能被继承且通过基类指针删除的类,析构函数必须声明为 virtual

六、override 与 final

6.1 override(显式重写)

classBase{public:virtualvoidfunc(){}};classDerived:publicBase{public:// override 标记:编译器检查确实重写了基类虚函数// 若拼写错误(如 funcc),编译器报错voidfunc()override{}};

6.2 final(禁止继承/重写)

#include<iostream>usingnamespacestd;classBase{public:virtualvoidshow(){cout<<"Base"<<endl;}};// final 类:禁止被继承classFinalClassfinal:publicBase{public:// final 虚函数:禁止子类重写voidshow()overridefinal{cout<<"FinalClass"<<endl;}};// class Child : public FinalClass {}; // 错误!FinalClass 是 finalintmain(){FinalClass fc;fc.show();// FinalClassreturn0;}

七、RTTI:dynamic_cast 与 typeid

7.1 dynamic_cast(安全向下转型)

#include<iostream>#include<typeinfo>usingnamespacestd;classAnimal{public:virtual~Animal(){}// 必须有虚函数才能 dynamic_cast};classDog:publicAnimal{public:voidbark(){cout<<"汪汪"<<endl;}};classCat:publicAnimal{public:voidmeow(){cout<<"喵喵"<<endl;}};intmain(){Animal*a1=newDog();Animal*a2=newCat();// dynamic_cast:安全的向下转型,失败返回 nullptrDog*d=dynamic_cast<Dog*>(a1);if(d){d->bark();// 汪汪}Dog*d2=dynamic_cast<Dog*>(a2);// a2 实际是 Catif(d2){d2->bark();// 不会执行}else{cout<<"a2 不是 Dog 类型,转型失败"<<endl;}deletea1;deletea2;return0;}

7.2 typeid(获取类型信息)

#include<iostream>#include<typeinfo>usingnamespacestd;classAnimal{public:virtual~Animal(){}};classDog:publicAnimal{};classCat:publicAnimal{};intmain(){Animal*a1=newDog();Animal*a2=newCat();cout<<"a1 实际类型: "<<typeid(*a1).name()<<endl;// class Dogcout<<"a2 实际类型: "<<typeid(*a2).name()<<endl;// class Catif(typeid(*a1)==typeid(Dog)){cout<<"a1 是 Dog"<<endl;}deletea1;deletea2;return0;}

八、实战:图形系统

综合本篇全部知识,实现完整图形系统:

#include<iostream>#include<vector>usingnamespacestd;// 抽象基类classShape{protected:string name;public:Shape(string n):name(n){}virtualdoublearea()const=0;// 纯虚函数virtualvoidinfo()const=0;virtual~Shape(){}// 虚析构};classCircle:publicShape{private:doubleradius;public:Circle(string n,doubler):Shape(n),radius(r){}doublearea()constoverride{return3.14159*radius*radius;}voidinfo()constoverride{cout<<name<<"(圆,半径 "<<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{cout<<name<<"(矩形 "<<w<<"x"<<h<<")面积: "<<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{cout<<name<<"(三角形)面积: "<<area()<<endl;}};intmain(){// 多态容器:基类指针数组管理多种派生对象vector<Shape*>shapes;shapes.push_back(newCircle("圆形",3));shapes.push_back(newRectangle("矩形",4,5));shapes.push_back(newTriangle("三角形",6,4));doubletotal=0;for(Shape*s:shapes){s->info();total+=s->area();}cout<<"总面积: "<<total<<endl;// 释放内存(虚析构保证正确)for(Shape*s:shapes){deletes;}return0;}

运行示例:

圆形(圆,半径 3)面积: 28.2743 矩形(矩形 4x5)面积: 20 三角形(三角形)面积: 12 总面积: 60.2743

总结

本篇讲解了继承的三种方式与构造析构顺序、多态三要素、虚函数与虚函数表、纯虚函数与抽象类、虚析构函数的重要性、override/final、RTTI(dynamic_cast/typeid),并用图形系统串联实战。重点掌握:public 继承的 is-a 语义、多态的实现条件、抽象类不能实例化、基类析构必须 virtual。

下一篇将讲解动态内存管理与智能指针(RAII、unique_ptr/shared_ptr/weak_ptr),敬请期待!

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

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

立即咨询