C++ 第11次课:继承进阶
主要内容:
- 多重继承
- 名词歧义
- 菱形继承
- 虚继承
- 继承中函数的重载和覆盖
1. 多重继承
一个子类可以同时继承多个父类,这种继承方式称为多重继承。子类将拥有所有父类的成员变量和成员函数。
代码示例:
c++
#include <iostream>
class Animal {
public:
void eat() {
std::cout << "Animal is eating." << std::endl;
}
};
class Mammal {
public:
void giveBirth() {
std::cout << "Mammal is giving birth." << std::endl;
}
};
class Bat : public Animal, public Mammal {
public:
void fly() {
std::cout << "Bat is flying." << std::endl;
}
};
int main() {
Bat bat;
bat.eat();
bat.giveBirth();
bat.fly();
return 0;
}
2. 名词歧义
当不同的父类拥有相同名称的成员,并且被多重继承到同一个子类时,就会发生名词歧义。编译器无法确定子类对象调用的是哪个父类的成员。
代码示例:
c++
#include <iostream>
class A {
public:
void print() {
std::cout << "Class A" << std::endl;
}
};
class B {
public:
void print() {
std::cout << "Class B" << std::endl;
}
};
class C : public A, public B {
public:
// 发生歧义,需要避免
// void print() {
// std::cout << "Class C" << std::endl;
// }
};
int main() {
C c;
//c.print(); // 发生歧义,编译错误
//c.A::print(); // 使用作用域分辨符指定调用A类的print()
//c.B::print(); // 使用作用域分辨符指定调用B类的print()
return 0;
}
3. 菱形继承
菱形继承指的是一个基类被两个或多个子类继承,而这些子类又共同派生出一个新的子类。这种继承关系的逻辑结构图类似于菱形。
菱形继承存在的问题: 间接基类会被间接子类重复继承,导致成员变量被构造两次甚至多次,造成数据冗余和浪费。
代码示例:
c++
#include <iostream>
class Person {
public:
Person(int age) : age(age) {}
int age;
};
class Student : public Person {
public:
Student(int age, int studentId) : Person(age), studentId(studentId) {}
int studentId;
};
class Teacher : public Person {
public:
Teacher(int age, int teacherId) : Person(age), teacherId(teacherId) {}
int teacherId;
};
class TeachingAssistant : public Student, public Teacher {
public:
TeachingAssistant(int age, int studentId, int teacherId) : Student(age, studentId), Teacher(age, teacherId) {}
};
int main() {
TeachingAssistant ta(25, 1001, 2001);
// 菱形继承,Person类的age成员被继承两次,存在问题
std::cout << "Age: " << ta.Student::age << std::endl; // 访问Student继承的age
std::cout << "Age: " << ta.Teacher::age << std::endl; // 访问Teacher继承的age
return 0;
}
4. 虚继承
为了解决菱形继承带来的问题,可以使用虚继承。虚继承使用 virtual
关键字修饰继承方式,可以写在 public
之前或之后。被虚继承的类称为虚基类。
代码示例:
c++
#include <iostream>
class Person {
public:
Person(int age) : age(age) {}
int age;
};
class Student : virtual public Person {
public:
Student(int age, int studentId) : Person(age), studentId(studentId) {}
int studentId;
};
class Teacher : virtual public Person {
public:
Teacher(int age, int teacherId) : Person(age), teacherId(teacherId) {}
int teacherId;
};
class TeachingAssistant : public Student, public Teacher {
public:
TeachingAssistant(int age, int studentId, int teacherId) : Student(age, studentId), Teacher(age, teacherId), Person(age) {}
// 需要在构造函数初始化列表中显式调用虚基类的构造函数
};
int main() {
TeachingAssistant ta(25, 1001, 2001);
// 虚继承,Person类的age成员只被继承一次
std::cout << "Age: " << ta.age << std::endl; // 直接访问age
return 0;
}
5. 继承中函数的重载和覆盖
重载: 函数名称相同,参数列表不同(参数数量或类型不同)。重载发生在同一个类域中,多个重载函数都可以被调用。
覆盖 (也称为隐藏): 内层作用域和外层作用域名称相同,内层覆盖外层。覆盖发生在不同的作用域,被覆盖的成员无法直接调用。 函数参数是否相同不影响覆盖,名称相同即可。成员函数和成员变量也可以发生覆盖。
代码示例:
c++
#include <iostream>
class Base {
public:
void print(int a) {
std::cout << "Base print(int): " << a << std::endl;
}
void print(int a, int b) {
std::cout << "Base print(int, int): " << a << ", " << b << std::endl;
}
int data = 10;
};
class Derived : public Base {
public:
void print(int a) { // 覆盖 Base::print(int)
std::cout << "Derived print(int): " << a << std::endl;
}
int data = 20; // 覆盖 Base::data
};
int main() {
Derived d;
d.print(5); // 调用 Derived::print(int)
d.Base::print(5, 10); // 调用 Base::print(int, int)
std::cout << "Derived data: " << d.data << std::endl; // 输出 20
std::cout << "Base data: " << d.Base::data << std::endl; // 输出 10
return 0;
}
课后作业
题目:
设计一个表示交通工具的继承体系,包含以下类:
Vehicle
(交通工具):- 具有私有成员 (型号名称) 和
int max_speed_
(最大速度)。 - 具有构造函数,接收型号名称和最大速度并进行初始化。
- 具有公共成员函数
display_info()
,用于打印交通工具的型号名称和最大速度。
- 具有私有成员 (型号名称) 和
LandVehicle
(陆地交通工具):- 公有继承自
Vehicle
。 - 具有私有成员
int num_wheels_
(轮子数量)。 - 具有构造函数,接收型号名称、最大速度和轮子数量并进行初始化。
- 重写
display_info()
函数,除了打印交通工具的基本信息外,还打印轮子数量。
- 公有继承自
WaterVehicle
(水上交通工具):- 公有继承自
Vehicle
。 - 具有私有成员
double displacement_
(排水量)。 - 具有构造函数,接收型号名称、最大速度和排水量并进行初始化。
- 重写
display_info()
函数,除了打印交通工具的基本信息外,还打印排水量。 - 具有公共成员函数
sail()
,打印 "水上交通工具正在航行"。
- 公有继承自
AmphibiousVehicle
(两栖交通工具):- 使用菱形继承,同时继承自
LandVehicle
和WaterVehicle
。 - 具有构造函数,接收型号名称、最大速度、轮子数量和排水量并进行初始化。
- 重写
display_info()
函数,打印所有相关信息。 - 重写
sail()
函数,打印 "l"。
- 使用菱形继承,同时继承自
要求:
- 实现上述类的定义和成员函数。
- 在
main()
函数中创建一个AmphibiousVehicle
对象,并调用其display_info()
和sail()
函数。 - 修改
LandVehicle
和WaterVehicle
的继承方式,使用虚继承来解决菱形继承的问题。 - 再次编译并运行程序,观察输出结果。
参考代码
cpp
#include <iostream>
#include <string>
class Vehicle {
public:
Vehicle(std::string model_name, int max_speed) : model_name_(model_name), max_speed_(max_speed) {
std::cout << "Vehicle constructor for " << model_name_ << std::endl;
}
~Vehicle() { std::cout << "Vehicle destructor for " << model_name_ << std::endl; }
void display_info() const {
std::cout << "Model: " << model_name_ << ", Max Speed: " << max_speed_ << std::endl;
}
protected:
std::string model_name_;
int max_speed_;
};
class LandVehicle : virtual public Vehicle { // 使用虚继承
public:
LandVehicle(std::string model_name, int max_speed, int num_wheels) : Vehicle(model_name, max_speed), num_wheels_(num_wheels) {
std::cout << "LandVehicle constructor for " << model_name << std::endl;
}
~LandVehicle() { std::cout << "LandVehicle destructor for " << model_name_ << std::endl; }
void display_info() const {
Vehicle::display_info();
std::cout << "Number of Wheels: " << num_wheels_ << std::endl;
}
protected:
int num_wheels_;
};
class WaterVehicle : virtual public Vehicle { // 使用虚继承
public:
WaterVehicle(std::string model_name, int max_speed, double displacement) : Vehicle(model_name, max_speed), displacement_(displacement) {
std::cout << "WaterVehicle constructor for " << model_name << std::endl;
}
~WaterVehicle() { std::cout << "WaterVehicle destructor for " << model_name_ << std::endl; }
void display_info() const {
Vehicle::display_info();
std::cout << "Displacement: " << displacement_ << std::endl;
}
void sail() const {
std::cout << "Water vehicle is sailing." << std::endl;
}
protected:
double displacement_;
};
class AmphibiousVehicle : public LandVehicle, public WaterVehicle {
public:
AmphibiousVehicle(std::string model_name, int max_speed, int num_wheels, double displacement)
: Vehicle(model_name, max_speed), // 最终派生类负责初始化虚基类
LandVehicle(model_name, max_speed, num_wheels),
WaterVehicle(model_name, max_speed, displacement) {
std::cout << "AmphibiousVehicle constructor for " << model_name << std::endl;
}
~AmphibiousVehicle() { std::cout << "AmphibiousVehicle destructor for " << model_name_ << std::endl; }
void display_info() const {
LandVehicle::display_info(); // 可以调用其中一个,因为 Vehicle 子对象共享
std::cout << "Displacement: " << displacement_ << std::endl; //可以直接访问
}
void sail() const {
std::cout << "Amphibious vehicle is sailing." << std::endl;
}
};
int main() {
AmphibiousVehicle av("水陆两栖车", 150, 4, 2.5);
av.display_info();
av.sail();
return 0;
}