什么是继承?
继承是 C++ 面向对象编程的三大特性之一(另外两个是封装和多态)。它允许我们创建一个新类(称为派生类或子类),从一个已有的类(称为基类或父类)中继承属性和方法。
核心思想:代码重用和建立类之间的层次关系。
优势:
- 代码重用:子类可以重用父类的代码,无需重新编写。
- 可扩展性:可以在子类中添加新的功能,扩展父类的能力。
- 逻辑清晰:建立 “is-a”(是一个)的关系,例如,
Caris-aVehicle。 - 易于维护:修改基类的代码,所有派生类都会自动继承这些更改。
继承的语法
class DerivedClass : access_specifier BaseClass {
// ... members of DerivedClass
};
DerivedClass:派生类名。BaseClass:基类名。access_specifier:访问修饰符,可以是public、protected或private。如果省略,默认为private(对于class)或public(对于struct)。
访问控制与继承方式
访问修饰符决定了基类成员在派生类中的访问级别。
| 继承方式 | 基类的 public 成员 |
基类的 protected 成员 |
基类的 private 成员 |
|---|---|---|---|
public |
在派生类中是 public |
在派生类中是 protected |
在派生类中不可访问 |
protected |
在派生类中是 protected |
在派生类中是 protected |
在派生类中不可访问 |
private |
在派生类中是 private |
在派生类中是 private |
在派生类中不可访问 |
public继承:最常用的方式。它建立了 “is-a” 的关系。派生类对象可以被当作基类对象使用。protected继承:基类的公有和保护成员都成为派生类的保护成员。private继承:基类的公有和保护成员都成为派生类的私有成员。这通常表示 “is-implemented-in-terms-of”(依据…来实现)的关系。
示例:
class Vehicle {
public:
void start() { /* ... */ }
protected:
int speed;
private:
int id;
};
class Car : public Vehicle {
public:
void drive() {
start(); // OK, start() is public in Car
speed = 100; // OK, speed is protected in Car
// id = 1; // Error: id is private in Vehicle and not accessible
}
};
int main() {
Car myCar;
myCar.start(); // OK, start() is public
// myCar.speed = 50; // Error: speed is protected in Car, not accessible from outside
return 0;
}
继承中的构造函数和析构函数
-
构造函数:
- 子类不能继承父类的构造函数。
- 创建子类对象时,会先调用父类的构造函数,然后再调用子类的构造函数。
- 如果父类有带参数的构造函数,子类必须在构造函数的初始化列表中显式调用它。
-
析构函数:
- 销毁子类对象时,会先调用子类的析构函数,然后再调用父类的析构函数(顺序与构造相反)。
- 重要:当使用基类指针指向派生类对象时,为了确保派生类的析构函数被正确调用,基类的析构函数必须声明为虚函数 (
virtual)。
示例:
#include
<iostream>
class Base {
public:
Base(int b) { std::cout << "Base constructor called with " << b << std::endl; }
virtual ~Base() { std::cout << "Base destructor called" << std::endl; }
};
class Derived : public Base {
public:
// Call Base's constructor in the initializer list
Derived(int d, int b) : Base(b) {
std::cout << "Derived constructor called with " << d << std::endl;
}
~Derived() override { std::cout << "Derived destructor called" << std::endl; }
};
int main() {
Base* ptr = new Derived(10, 20);
delete ptr; // Correctly calls ~Derived() then ~Base() because of virtual destructor
return 0;
}
// Output:
// Base constructor called with 20
// Derived constructor called with 10
// Derived destructor called
// Base destructor called
函数重写与多态
- 函数重写 (Overriding):派生类可以提供一个与基类中某个虚函数(
virtualfunction)具有相同签名(函数名、参数列表、const属性)的新实现。 - 虚函数 (
virtual):在基类中用virtual关键字声明的函数。它告诉编译器,这个函数可能会在派生类中被重写,并且应该在运行时根据对象的实际类型来决定调用哪个版本(动态绑定)。 - 多态 (Polymorphism):允许我们使用基类的指针或引用来调用派生类中重写的函数,从而实现 “一个接口,多种形态” 的效果。
C++11 关键字:
override:在派生类函数后添加override,可以让编译器检查该函数是否真的重写了基类的虚函数。如果不是,编译器会报错。这是一个很好的实践,可以防止拼写错误等问题。final:- 用于虚函数:表示该虚函数不能再被任何后续派生类重写。
- 用于类:
class MyClass final { ... };表示该类不能被继承。
示例:
#include
<iostream>
class Animal {
public:
virtual void makeSound() const {
std::cout << "Some generic animal sound" << std::endl;
}
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void makeSound() const override { // 'override' ensures we are overriding a base virtual function
std::cout << "Woof!" << std::endl;
}
};
class Cat : public Animal {
public:
void makeSound() const override {
std::cout << "Meow!" << std::endl;
}
};
void playSound(const Animal& animal) {
animal.makeSound(); // Polymorphic call
}
int main() {
Dog dog;
Cat cat;
playSound(dog); // Outputs: Woof!
playSound(cat); // Outputs: Meow!
return 0;
}
抽象类与纯虚函数
- 纯虚函数 (Pure Virtual Function):一个没有实现的虚函数,通过在函数声明末尾加上
= 0来定义。 - 抽象类 (Abstract Class):包含至少一个纯虚函数的类。
- 不能被实例化(不能创建对象)。
- 主要用作接口,强制派生类必须实现其纯虚函数。如果派生类没有实现所有纯虚函数,那么它自己也成为一个抽象类。
示例:
class Shape { // Abstract class
public:
virtual double getArea() const = 0; // Pure virtual function
virtual ~Shape() = default;
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double getArea() const override { // Must implement
return width * height;
}
};
int main() {
// Shape s; // Error: cannot instantiate abstract class
Rectangle r(10, 5);
Shape* shape_ptr = &r;
std::cout << "Area: " << shape_ptr->getArea() << std::endl; // Outputs: Area: 50
return 0;
}
多重继承与菱形问题
-
多重继承 (Multiple Inheritance):一个派生类可以同时从多个基类继承。
class Derived : public Base1, public Base2 { // ... }; -
菱形问题 (Diamond Problem):当两个类(如
B和C)都从同一个基类(A)继承,而另一个类(D)又同时从B和C继承时,D中会包含两份来自A的成员,导致歧义。A / \ B C \ / D -
虚继承 (Virtual Inheritance):用于解决菱形问题。通过在继承时使用
virtual关键字,可以确保在派生类中只保留一份共享基类的成员。
示例:
class A {
public:
int data;
};
// Use virtual inheritance
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
int main() {
D d;
d.data = 100; // No ambiguity, there is only one 'data' member in D
return 0;
}
正文完


