继承和多态
继承的概念
继承是面向对象编程的一个重要特性,它允许新创建的类(称为子类或派生类)继承现有类(称为基类或父类)的属性和方法。
基类与子类
基类:提供了可以被继承的属性和方法。
子类:继承了基类的属性和方法,并且可以添加新的属性和方法或重写继承的方法。
继承的类型
公有继承(public):子类继承基类的公有成员和保护成员,并且它们在子类中也是公有或保护的。
私有继承(private):子类继承基类的公有和保护成员,但它们在子类中变为私有的。
保护继承(protected):子类继承基类的公有和保护成员,但它们在子类中变为保护的。
多态性
多态性是对象可以有多种形式的能力。在C++中,多态性主要通过虚函数实现。
虚函数
虚函数是基类中的函数,允许在派生类中被重写(Override)。使用 virtual 关键字声明。
class Base {
public:virtual void show() { std::cout << "Base show" << std::endl; }
};class Derived : public Base {
public:void show() override { std::cout << "Derived show" << std::endl; }
};
抽象类
抽象类是不能被实例化的类,它至少包含一个纯虚函数。使用 = 0 来定义纯虚函数。
class AbstractBase {
public:virtual void pureVirtualFunc() = 0; // 纯虚函数
};class ConcreteClass : public AbstractBase {
public:void pureVirtualFunc() override {// 实现抽象类中的纯虚函数}
};
多态的实现
使用基类指针或引用指向派生类对象,可以实现动态多态性。
Base* basePtr = new Derived();
basePtr->show(); // 输出 "Derived show",展示了多态性
delete basePtr;
虚析构函数
在C++中,如果一个类是多态基类(即有一个或多个虚函数),则其析构函数应当被声明为虚函数。这是因为当通过基类指针删除派生类对象时,需要确保正确地调用派生类的析构函数,然后才是基类的析构函数。
class Base {
public:virtual ~Base() {std::cout << "Base destructor" << std::endl;}
};class Derived : public Base {
public:~Derived() {std::cout << "Derived destructor" << std::endl;}
};int main() {Base* basePtr = new Derived();delete basePtr; // 正确地调用Derived和Base的析构函数return 0;
}
输出为
Derived destructor
Base destructor
运算符重载与多态
运算符重载允许你定义类的运算符如何工作。当运算符被重载为成员函数时,它可以是虚函数,从而支持多态性。
例子
- 使用虚函数重载 == 运算符
class Base {
public:virtual bool operator==(const Base& other) const {// 默认实现,可能需要根据实际需求重写return this == &other;}
};class Derived : public Base {int value;
public:Derived(int v) : value(v) {}bool operator==(const Base& other) const override {// 重写 == 运算符以比较 Derived 对象const Derived* otherDerived = dynamic_cast<const Derived*>(&other);if (otherDerived) {return value == otherDerived->value;}return false;}
};
- 重载输出流运算符 <<
class Person {std::string name;
public:Person(const std::string& n) : name(n) {}friend std::ostream& operator<<(std::ostream& os, const Person& p) {os << "Person: " << p.name;return os;}
};class Employee : public Person {int id;
public:Employee(const std::string& n, int i) : Person(n), id(i) {}friend std::ostream& operator<<(std::ostream& os, const Employee& e) {os << static_cast<const Person&>(e); // 调用基类的输出os << ", ID: " << e.id;return os;}
};
在这个例子中,Employee 类重载了 << 运算符,输出 Employee 对象时,首先调用了基类 Person 的 << 运算符,然后添加了 Employee 特有的信息。
- 重载赋值运算符 =
class Data {int* array;size_t size;
public:Data(size_t s) : size(s), array(new int[s]) {}Data(const Data& other) : size(other.size), array(new int[other.size]) {std::copy(other.array, other.array + size, array);}Data& operator=(const Data& other) {if (this != &other) {delete[] array;size = other.size;array = new int[size];std::copy(other.array, other.array + size, array);}return *this;}~Data() {delete[] array;}
};
在这个例子中,赋值运算符 = 被重载以确保深拷贝数组,并且正确地处理自赋值的情况。