往期回顾:
一、前言
在前面文章的学习中,我们了解了类和对象的基础知识、构造函数、拷贝构造函数、静态成员、常量成员、运算符重载、友元函数和友元类。今天,我们将学习 C++ 中的继承和派生类。继承是面向对象编程中的一个重要概念,它允许我们创建一个新的类,该类继承一个或多个现有类的属性和方法,从而实现代码的重用和扩展。
二、 继承和派生类
2.1、什么是继承?
继承(Inheritance)是面向对象编程(OOP)中一个核心概念,它为代码的复用和组织提供了一种强有力的机制。通过继承,我们可以创建一个新的类(派生类或子类),这个新类继承了另一个已存在类(基类或父类)的属性和行为(即成员变量和方法)。继承使得子类可以复用父类的代码,同时还可以添加新的属性和方法或覆盖(重写)父类中的方法,以满足特定的需求。
继承不仅减少了代码冗余,提高了代码的可维护性,还促进了多态性的实现,使得不同类的对象可以通过统一的接口进行操作。
语法:
在 C++ 中,继承关系是通过在派生类的定义中,使用冒号(:)后跟基类名来指定的。基本语法如下:
class BaseClass {
// 基类的成员变量和成员函数
};
class DerivedClass : public BaseClass {
// 派生类的成员变量和成员函数
// 这里可以添加新的成员,也可以重写基类的成员函数
};
在上述示例中,DerivedClass
是通过继承 BaseClass
创建的。
示例:
我们定义一个基类 Person
,表示一个人的基本信息,然后定义一个派生类 Student
,表示学生的特有信息。
#include <iostream>
using namespace std;
// 基类 Person
class Person {
public:
string name;
int age;
Person(string n, int a) : name(n), age(a) {}
void displayInfo() {
cout << "Name: " << name << endl;
cout << "Age: " << age << endl;
}
};
// 派生类 Student
class Student : public Person {
public:
string school;
Student(string n, int a, string s) : Person(n, a), school(s) {}
void displayStudentInfo() {
displayInfo();
cout << "School: " << school << endl;
}
};
int main() {
Student student("John", 20, "XYZ University");
student.displayStudentInfo();
return 0;
}
在这个示例中,Student
类继承自 Person
类,因此它具有 Person
类的所有成员变量和成员函数。我们在 Student
类中添加了一个新的成员变量 school
和一个新的成员函数 displayStudentInfo
。
2.2、 访问控制
在继承中,基类的成员可以根据其访问控制权限决定派生类对它们的访问权限。访问控制有三种:public
、protected
和 private
。
|
当派生类以 public 方式继承基类时,基类的 public 成员在派生类中保持 public ,基类的 protected 成员在派生类中保持 protected ,基类的 private 成员在派生类中不可访问。 |
|
当派生类以 protected 方式继承基类时,基类的 public 成员和 protected 成员在派生类中都变成 protected ,基类的 private 成员在派生类中不可访问。 |
|
当派生类以 private 方式继承基类时,基类的 public 成员和 protected 成员在派生类中都变成 private ,基类的 private 成员在派生类中不可访问。 |
示例:
我们使用相同的 Person
和 Student
类来演示不同的继承方式。
#include <iostream>
using namespace std;
class Person {
public:
string name;
int age;
Person(string n, int a) : name(n), age(a) {}
void displayInfo() {
cout << "Name: " << name << endl;
cout << "Age: " << age << endl;
}
};
class StudentPublic : public Person {
public:
string school;
StudentPublic(string n, int a, string s) : Person(n, a), school(s) {}
void displayStudentInfo() {
displayInfo();
cout << "School: " << school << endl;
}
};
class StudentProtected : protected Person {
public:
string school;
StudentProtected(string n, int a, string s) : Person(n, a), school(s) {}
void displayStudentInfo() {
displayInfo();
cout << "School: " << school << endl;
}
};
class StudentPrivate : private Person {
public:
string school;
StudentPrivate(string n, int a, string s) : Person(n, a), school(s) {}
void displayStudentInfo() {
displayInfo();
cout << "School: " << school << endl;
}
};
int main() {
StudentPublic studentPublic("John", 20, "XYZ University");
studentPublic.displayStudentInfo();
cout << "Accessing public inherited member: " << studentPublic.name << endl;
StudentProtected studentProtected("Alice", 21, "ABC University");
studentProtected.displayStudentInfo();
// cout << "Accessing protected inherited member: " << studentProtected.name << endl; // 错误
StudentPrivate studentPrivate("Bob", 22, "DEF University");
studentPrivate.displayStudentInfo();
// cout << "Accessing private inherited member: " << studentPrivate.name << endl; // 错误
return 0;
}
在这个示例中,我们定义了三个派生类 StudentPublic
、StudentProtected
和 StudentPrivate
,分别使用 public
、protected
和 private
继承方式。可以看到,不同的继承方式会影响派生类对基类成员的访问权限。
选择哪种继承方式取决于具体的设计需求,但大多数情况下,公有继承是最常用的。
继承还允许我们实现接口继承和实现继承。在接口继承中,我们主要继承基类的接口(即方法签名),而不关心实现细节。而在实现继承中,我们不仅继承了基类的接口,还继承了其实现代码,并可以在派生类中对这些实现进行扩展或修改。
2.3、 多重继承
(1)什么是多重继承
C++ 支持一个类从多个基类继承,这种特性被称为多重继承(Multiple Inheritance)。多重继承是面向对象编程中一个强大但复杂的概念,它允许派生类同时继承多个基类的属性和方法。这种机制为类的设计提供了更高的灵活性和表达力,但也引入了额外的复杂性和潜在的问题,如命名冲突(也称为菱形继承问题或钻石继承问题)和对象切片等。
语法
在 C++ 中,多重继承的语法与单继承类似,只是在派生类的定义中指定了多个基类。使用逗号(,)分隔多个基类。以下是多重继承的基本语法示例:
class Base1 {
// Base1 的成员
};
class Base2 {
// Base2 的成员
};
class Derived : public Base1, public Base2 {
// Derived 继承自 Base1 和 Base2
// Derived 可以添加新的成员,也可以重写 Base1 和 Base2 中的成员函数
};
在这个例子中,Derived
类同时继承了 Base1
和 Base2
两个基类。它可以使用这两个基类中的所有公有和保护成员(除非在派生类中被重写)。
(2)命名冲突
当多个基类中包含同名的成员时,多重继承会导致命名冲突。为了解决这个问题,C++ 提供了作用域解析运算符(::)来明确指定要访问的成员属于哪个基类。例如:
Derived obj;
obj.Base1::memberFunction(); // 调用 Base1 中的 memberFunction
obj.Base2::memberFunction(); // 调用 Base2 中的 memberFunction
(3)菱形继承问题(钻石继承问题)
菱形继承是多重继承中一个常见的问题,它发生在多个基类最终都继承自同一个基类,但路径不同的情况下。这会导致在派生类中存在多个基类的副本,从而浪费内存并可能引发其他问题。为了解决这个问题,C++ 提供了虚基类(Virtual Base Class)的概念。
(4)虚基类
在 C++ 中,可以将基类声明为虚基类,以解决菱形继承问题。当基类被声明为虚基类时,无论它在继承层次结构中出现多少次,派生类中都只会有一个该基类的实例。这通过确保基类在继承树中的唯一性来减少内存浪费,并避免潜在的歧义。
class VirtualBase {
// VirtualBase 的成员
};
class Base1 : virtual VirtualBase {
// Base1 继承自 VirtualBase 作为虚基类
};
class Base2 : virtual VirtualBase {
// Base2 也继承自 VirtualBase 作为虚基类
};
class Derived : public Base1, public Base2 {
// Derived 继承自 Base1 和 Base2,它们都继承自 VirtualBase 作为虚基类
// 这里只会有一个 VirtualBase 的实例
};
在这个例子中,Derived
类通过 Base1
和 Base2
间接继承了 VirtualBase
,但由于 VirtualBase
被声明为虚基类,Derived
类中只会有一个 VirtualBase
的实例。这解决了菱形继承问题,并确保了基类成员的唯一性和一致性。
示例:
我们定义两个基类 Person
和 Employee
,然后定义一个派生类 Manager
,它继承自 Person
和 Employee
。
#include <iostream>
using namespace std;
class Person {
public:
string name;
int age;
Person(string n, int a) : name(n), age(a) {}
void displayPersonInfo() {
cout << "Name: " << name << endl;
cout << "Age: " << age << endl;
}
};
class Employee {
public:
int employeeID;
Employee(int id) : employeeID(id) {}
void displayEmployeeInfo() {
cout << "Employee ID: " << employeeID << endl;
}
};
class Manager : public Person, public Employee {
public:
Manager(string n, int a, int id) : Person(n, a), Employee(id) {}
void displayManagerInfo() {
displayPersonInfo();
displayEmployeeInfo();
}
};
int main() {
Manager manager("John", 35, 12345);
manager.displayManagerInfo();
return 0;
}
在这个示例中,我们定义了一个 Manager
类,它同时继承自 Person
和 Employee
。Manager
类拥有 Person
和 Employee
的所有成员,可以调用它们的成员函数。
以上就是 C++ 程序的继承和派生类的基础知识点了。包括不同的继承方式和多重继承。继承是面向对象编程中的一个重要特性,它允许我们创建一个新的类,该类继承一个或多个现有类的属性和方法,从而实现代码的重用和扩展。
都看到这里了,点个赞再走呗朋友~
加油吧,预祝大家变得更强!