继承和多态
继承的概念
继承是面向对象编程的一个重要特性,它允许新创建的类(称为子类或派生类)继承现有类(称为基类或父类)的属性和方法。
基类与子类
基类:提供了可以被继承的属性和方法。
子类:继承了基类的属性和方法,并且可以添加新的属性和方法或重写继承的方法。
继承的类型
公有继承(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;
}
};
在这个例子中,赋值运算符 = 被重载以确保深拷贝数组,并且正确地处理自赋值的情况。