C++面向对象编程之多态
什么是多态
C++中的多态是面向对象编程的三个特性之一(另外两个分别是封装和继承),多态允许使用统一的接口来操作不同的数据类型,从而增加代码的灵活性和可复用性。
多态分为两种:
- 编译时多态(静态多态): 通过函数重载和运算符重载实现
- 运行时多态(动态多态): 通过虚函数和继承实现
运行时多态(动态多态)
当基类中的函数被声明为虚函数时,派生类可以重写该函数,然后通过基类指针或引用来调用该函数,而实际中调用的是派生类重写的函数
虚函数
在基类中,使用virtual声明的成员函数就是虚函数,在派生类中,可以使用override关键字(C++11中引入)来显示的重写虚函数
#include <iostream>
using namespace std;
class Animal {
public:
virtual void speak() { // 虚函数
cout << "animal speaks!" << endl;
}
virtual ~Animal() {} // 虚析构函数
};
class Dog : public Animal {
public:
void speak() override { // override关键字是C++11中引入的,C++98/03中是没有override关键字的,使用override关键字也不会出现问题
cout << "woof woof!" << endl;
}
};
class Cat : public Animal {
public:
void speak() override {
cout << "meow meow!" << endl;
}
};
int main() {
Animal* dog = new Dog();
Animal* cat = new Cat();
dog->speak();
cat->speak();
delete dog;
delete cat;
return 0;
}
override关键字使用说明
override关键字是C++11中引入的,在C++98/03中没有,通过函数名称找到对应的重写函数,此时如果函数签名有误,比如参数不一致,相当于派生类中创建了一个新函数,而不是被认为重新来基类的虚函数
class Dog : public Animal {
public:
// 错误:本想重写speak,但写成了speak(参数不同),没有override关键字,编译器不会报错
void speak(int volume) {
cout << "Woof! Woof! with volume " << volume << endl;
}
};
使用override关键字的一个好处是避免程序产生预料之外的行为,例如上面的代码中参数不同,main函数中调用的将会是基类的speak()方法,与预期行为不符,如果使用了override关键字,编译会报错,这样可以避免不符合预期的行为发生
class Dog : public Animal {
public:
// 错误:函数签名不匹配,且使用了override,编译器会报错
void speak(int volume) override {
cout << "Woof! Woof! with volume " << volume << endl;
}
};
实际项目中的使用考量
- 不使用
override关键字- 维护老版本C++代码(C++98/03)
- 简单的个人项目
- 团队约定不使用C++11特性
- 推荐使用
override关键字- 新项目开发
- 大型团队协作
- 需要高质量代码的项目
- 避免潜在的重写错误
纯虚函数和抽象类
如果基类中的虚函数没有具体实现,可以将其声明为纯虚函数,包含纯虚函数的类称为抽象类(只要有一个函数是纯虚函数,其它函数就算不是纯虚函数,也是一个抽象类)抽象类不能被实例化,之类作为基类被继承。
纯虚函数的一般定义如下:
virtual 返回类型 函数名(参数列表) = 0;
抽象类定义
#include <iostream>
using namespace std;
class Shape {
public:
// 虚函数
virtual double area() const = 0;
virtual double perimeter() const = 0;
// 虚析构含数
virtual ~Shape() = default;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override {
return 3.14159 * radius * radius;
}
double perimeter() const override {
return 2 * 3.14159 * radius;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override {
return width * height;
}
double perimeter() const override {
return (width + height) * 2;
}
};
工作原理
多态是通过虚函数表(vtable)和虚函数指针(vptr)实现的,当类中存在虚函数时,编译器会为该类创建一个虚函数表,表中存放着虚函数的地址,同时,每个实例化对象会持有一个指向该虚函数表的指针,当通过基类指针调用虚函数时,程序会根据实际对象的虚函数指针找到对应的虚函数表,然后调用正确的函数
多态的应用场景
-
统一接口处理不同对象
void printArea(const Shape& shape) { cout << "Area: " << shape.area() << endl; } int main() { Circle circle(5.0); Rectangle rect(4.0, 6.0); printArea(circle); // 输出圆的面积 printArea(rect); // 输出矩形的面积 return 0; } -
工厂模式
class AnimalFactory { public: static Animal* createAnimal(const string& type) { if (type == "dog") return new Dog(); if (type == "cat") return new Cat(); return nullptr; } };
虚析构函数
必要性
class Base {
public:
Base() { cout << "Base constructor" << endl; }
virtual ~Base() { cout << "Base destructor" << endl; } // 虚析构函数
};
class Derived : public Base {
public:
Derived() { cout << "Derived constructor" << endl; }
~Derived() override { cout << "Derived destructor" << endl; }
};
int main() {
Base* obj = new Derived();
delete obj; // 正确调用Derived和Base的析构函数
return 0;
}
final关键字(C++11)
final关键字是从C++11中引入,用于类或者虚函数,用于类时,表示该类不能被继承,用于虚函数时,表示该虚函数不能被重写
class Base final { // 类不能被继承
// ...
};
class Base {
public:
virtual void func() final {} // 函数不能被重写
};
// class Derived : public Base {}; // 错误!Base是final的
动态类型识别
typeid和dynamic_cast
#include <typeinfo>
class Animal {
public:
virtual ~Animal() = default;
};
class Dog : public Animal {};
class Cat : public Animal {};
void identifyAnimal(Animal* animal) {
if (dynamic_cast<Dog*>(animal)) {
cout << "This is a Dog" << endl;
} else if (dynamic_cast<Cat*>(animal)) {
cout << "This is a Cat" << endl;
}
cout << "Type: " << typeid(*animal).name() << endl;
}
多态的优势
- 复用性: 使用统一的接口管理不同的对象
- 可扩展性: 容易添加新的派生类
- 灵活性: 运行时动态决定调用哪个对象
- 可维护性: 降低代码耦合度
更多推荐


所有评论(0)