C++继承与多态:
作为C++面向对象编程(OOP)的三大支柱之一,继承与多态是实现代码复用、拓展性和灵活性的关键。对于初学C++的同学来说,这两个概念既是重点也是难点。本文将从基础概念入手,结合代码示例和实战场景,带你一步步掌握继承与多态的核心用法。
一、继承:站在巨人的肩膀上
1. 什么是继承?
继承是指一个类(派生类/子类)可以继承另一个类(基类/父类)的属性和方法。通过继承,子类可以复用父类的代码,同时还能根据需求扩展或修改功能,大大减少了代码冗余。
2. 继承的语法
// 基类
class 基类名 {
// 成员变量和成员函数
};
// 派生类
class 派生类名 : 继承方式 基类名 {
// 子类独有的成员
};
继承方式有三种:
• public:公有权限继承,父类的public和protected成员在子类中保持原权限,private成员不可访问。
• protected:保护权限继承,父类的public和protected成员在子类中变为protected,private成员不可访问。
• private:私有权限继承,父类的public和protected成员在子类中变为private,private成员不可访问。
注意:子类永远无法访问父类的private成员,如果需要让子类访问,可以将成员权限设为protected。
3. 继承的示例:动物类与猫类
#include <iostream>
#include <string>
using namespace std;
// 基类:动物
class Animal {
protected:
string name;
int age;
public:
Animal(string n, int a) : name(n), age(a) {}
void eat() {
cout << name << " 在吃东西" << endl;
}
};
// 派生类:猫,公有继承Animal
class Cat : public Animal {
private:
string color; // 子类独有的属性
public:
// 子类构造函数必须调用父类构造函数
Cat(string n, int a, string c) : Animal(n, a), color(c) {}
// 子类独有的方法
void catchMouse() {
cout << color << "的" << name << " 在抓老鼠" << endl;
}
// 重写父类的方法(方法覆盖)
void eat() {
cout << color << "的" << name << " 在吃猫粮" << endl;
}
};
int main() {
Cat cat("小白", 2, "白色");
cat.eat(); // 调用子类重写的eat方法
cat.catchMouse(); // 调用子类独有的方法
return 0;
}
运行结果:
白色的小白 在吃猫粮
白色的小白 在抓老鼠
4. 继承中的构造与析构顺序
• 构造顺序:先调用父类的构造函数,再调用子类的构造函数。
• 析构顺序:先调用子类的析构函数,再调用父类的析构函数。
二、多态:同一接口,不同实现
1. 什么是多态?
多态是指同一操作作用于不同的对象时,会产生不同的执行结果。简单来说,就是“一个接口,多种实现”。C++中的多态主要分为两种:
• 静态多态:编译时多态,通过函数重载和模板实现。
• 动态多态:运行时多态,通过虚函数和继承实现。
本文重点讲解动态多态,这是C++多态的核心。
2. 动态多态的实现条件
要实现动态多态,必须满足以下三个条件:
1. 存在继承关系。
2. 子类重写父类的虚函数(函数名、参数列表、返回值类型完全一致)。
3. 使用父类的指针或引用指向子类对象。
3. 虚函数与纯虚函数
(1)虚函数
在父类的成员函数前加上virtual关键字,该函数就成为虚函数。子类可以重写虚函数,实现自己的逻辑。
class Animal {
public:
virtual void makeSound() { // 虚函数
cout << "动物发出声音" << endl;
}
};
(2)纯虚函数
如果父类的虚函数没有具体实现,可以将其声明为纯虚函数,格式为virtual 函数名(参数列表) = 0;。包含纯虚函数的类称为抽象类,抽象类不能实例化对象,只能作为基类被继承。
class Animal {
public:
virtual void makeSound() = 0; // 纯虚函数
};
4. 多态的示例:动物叫
#include <iostream>
#include <string>
using namespace std;
// 抽象类:Animal
class Animal {
protected:
string name;
public:
Animal(string n) : name(n) {}
// 纯虚函数,子类必须重写
virtual void makeSound() = 0;
};
// 子类:Cat
class Cat : public Animal {
public:
Cat(string n) : Animal(n) {}
// 重写纯虚函数
void makeSound() override { // override关键字显式声明重写,建议加上
cout << name << " 喵喵叫" << endl;
}
};
// 子类:Dog
class Dog : public Animal {
public:
Dog(string n) : Animal(n) {}
// 重写纯虚函数
void makeSound() override {
cout << name << " 汪汪叫" << endl;
}
};
int main() {
// 父类指针指向子类对象
Animal* animal1 = new Cat("小白");
Animal* animal2 = new Dog("小黑");
animal1->makeSound(); // 输出:小白 喵喵叫
animal2->makeSound(); // 输出:小黑 汪汪叫
// 释放内存
delete animal1;
delete animal2;
return 0;
}
运行结果:
小白 喵喵叫
小黑 汪汪叫
5. 虚析构函数
当使用父类指针指向子类对象并删除时,如果父类的析构函数不是虚函数,只会调用父类的析构函数,导致子类的析构函数无法被调用,从而引发内存泄漏。
解决方法:将父类的析构函数声明为虚析构函数。
class Animal {
public:
virtual ~Animal() { // 虚析构函数
cout << "Animal析构函数" << endl;
}
};
三、继承与多态的常见误区
1. 混淆重写(Override)与重载(Overload)
• 重写:子类重写父类的虚函数,函数签名(函数名、参数列表、返回值)必须完全一致,发生在父子类之间。
• 重载:同一作用域内,函数名相同,参数列表不同(个数、类型、顺序),与返回值无关。
2. 忘记使用虚函数
如果父类的函数没有声明为虚函数,即使子类重写了该函数,使用父类指针调用时,仍然会执行父类的函数,无法实现动态多态。
3. 直接实例化抽象类
抽象类包含纯虚函数,不能直接创建对象,只能作为基类被子类继承,子类必须重写所有纯虚函数才能实例化。
四、实战场景:图形面积计算
假设我们需要计算不同图形(圆形、矩形、三角形)的面积,使用继承与多态可以轻松实现。
#include <iostream>
using namespace std;
// 抽象类:图形
class Shape {
public:
// 纯虚函数:计算面积
virtual double getArea() = 0;
};
// 子类:圆形
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double getArea() override {
return 3.14 * radius * radius;
}
};
// 子类:矩形
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double getArea() override {
return width * height;
}
};
int main() {
Shape* shape1 = new Circle(5);
Shape* shape2 = new Rectangle(4, 6);
cout << "圆形面积:" << shape1->getArea() << endl;
cout << "矩形面积:" << shape2->getArea() << endl;
delete shape1;
delete shape2;
return 0;
}
运行结果:
圆形面积:78.5
矩形面积:24
五、总结
1. 继承实现了代码复用,子类可以继承父类的属性和方法,并扩展自己的功能。
2. 多态实现了接口复用,通过虚函数和父类指针/引用,让不同子类对象以统一的接口呈现不同的行为。
3. 动态多态的三个条件:继承、虚函数重写、父类指针/引用指向子类对象。
4. 抽象类包含纯虚函数,不能实例化,用于定义统一的接口。
继承与多态是C++面向对象编程的核心,掌握它们可以让你的代码更加灵活、可扩展。建议同学们多写代码练习,结合实战场景理解这两个概念的应用。
六、课后练习
1. 基于本文的图形面积示例,添加三角形类,实现面积计算。
2. 设计一个交通工具类(Vehicle),包含纯虚函数run(),然后派生出汽车(Car)、自行车(Bike)、飞机(Plane)类,重写run()方法,实现多态。
2911

被折叠的 条评论
为什么被折叠?



