一个类从另一个类派生属性和特征的能力称为继承。继承是面向对象编程最重要的特性之一。
**子类:**从另一个类继承属性的类称为子类或派生类。
**超类:**属性被子类继承的类称为基类或超类。
文章分为以下子主题:
- [为什么以及何时使用继承?](https://www.geeksforgeeks.org/inheritance-in-c/?ref=leftbar-rightbar#Why and when to use inheritance?)
- [继承方式](https://www.geeksforgeeks.org/inheritance-in-c/?ref=leftbar-rightbar#Modes of Inheritance)
- [继承类型](https://www.geeksforgeeks.org/inheritance-in-c/?ref=leftbar-rightbar#Types of Inheritance)
为什么以及何时使用继承?
考虑一组车辆。您需要为 Bus、Car 和 Truck 创建类。FuelAmount()、capacity()、applyBrakes() 方法对于所有三个类都是相同的。如果我们创建这些类避免继承,那么我们必须在三个类中的每一个中编写所有这些函数,如下图所示:
您可以清楚地看到上述过程导致相同代码重复 3 次。这增加了错误和数据冗余的机会。为了避免这种情况,使用了继承。如果我们创建一个Vehicle类并在其中编写这三个函数并从车辆类中继承其余的类,那么我们可以简单地避免数据重复并增加可重用性。看下图,其中三个类继承自车辆类:
使用继承,我们只需要编写一次函数而不是三次,因为我们从基类(Vehicle)继承了其余三个类。
在 C++ 中实现继承:为了创建从基类继承的子类,我们必须遵循以下语法。
语法:
class subclass_name : access_mode base_class_name
{
// 子类的主体
};
这里,subclass_name是子类的名称,access_mode是您想要继承该子类的模式,例如:public、private 等,base_class_name是您想要继承子类的基类的名称.
注意:派生类不会继承对私有数据成员的***访问权限。***但是,它确实继承了一个完整的父对象,其中包含该类声明的任何私有成员。
- CPP
// C++ program to demonstrate implementation
// of Inheritance
#include <bits/stdc++.h>
using namespace std;
// Base class
class Parent
{
public:
int id_p;
};
// Sub class inheriting from Base Class(Parent)
class Child : public Parent
{
public:
int id_c;
};
// main function
int main()
{
Child obj1;
// An object of class child has all data members
// and member functions of class parent
obj1.id_c = 7;
obj1.id_p = 91;
cout << "Child id is: " << obj1.id_c << '\n';
cout << "Parent id is: " << obj1.id_p << '\n';
return 0;
}
输出
孩子 id 是 7
父母 id 是 91
在上面的程序中,‘Child’ 类是从’Parent’ 类公开继承的,因此’Parent’ 类的公共数据成员也将由’Child’ 类继承。
继承方式
- 公共模式:如果我们从公共基类派生一个子类。然后基类的公共成员将在派生类中变为公共,而基类的受保护成员将在派生类中变为受保护。
- 保护模式:如果我们从受保护的基类派生一个子类。然后基类的公共成员和受保护成员都将在派生类中受到保护。
- 私有模式:如果我们从私有基类派生一个子类。然后基类的公共成员和受保护成员都将在派生类中变为私有。
**注意:**基类中的私有成员不能在派生类中直接访问,而受保护的成员可以直接访问。例如,类 B、C 和 D 都包含以下示例中的变量 x、y 和 z。这只是访问的问题。
// C++ Implementation to show that a derived class
// doesn’t inherit access to private data members.
// However, it does inherit a full parent object.
class A
{
public:
int x;
protected:
int y;
private:
int z;
};
class B : public A
{
// x is public
// y is protected
// z is not accessible from B
};
class C : protected A
{
// x is protected
// y is protected
// z is not accessible from C
};
class D : private A // 'private' is default for classes
{
// x is private
// y is private
// z is not accessible from D
};
下表总结了上述三种模式,并显示了在公共、保护和私有模式下派生时子类中基类成员的访问说明符:
C++ 中的继承类型
1. 单一继承:在单一继承中,一个类只能从一个类继承。即一个子类仅由一个基类继承。
语法:
class subclass_name : access_mode base_class
{
// 子类的主体
};
- CPP
// C++ program to explain
// Single inheritance
#include<iostream>
using namespace std;
// base class
class Vehicle {
public:
Vehicle()
{
cout << "This is a Vehicle\n";
}
};
// sub class derived from a single base classes
class Car : public Vehicle {
};
// main function
int main()
{
// Creating object of sub class will
// invoke the constructor of base classes
Car obj;
return 0;
}
输出
这是一辆车
2.多重继承:多重继承是C++的一个特性,一个类可以从多个类继承。即一个子类继承自多个基类。
语法:
class subclass_name : access_mode base_class1, access_mode base_class2, ....
{
// 子类的主体
};
在这里,基类的数量将用逗号(’,’)分隔,并且必须指定每个基类的访问模式。
- CPP
// C++ program to explain
// multiple inheritance
#include<iostream>
using namespace std;
// first base class
class Vehicle {
public:
Vehicle()
{
cout << "This is a Vehicle\n";
}
};
// second base class
class FourWheeler {
public:
FourWheeler()
{
cout << "This is a 4 wheeler Vehicle\n";
}
};
// sub class derived from two base classes
class Car : public Vehicle, public FourWheeler {
};
// main function
int main()
{
// Creating object of sub class will
// invoke the constructor of base classes.
Car obj;
return 0;
}
输出
这是一辆汽车
这是一辆四轮车
请访问此链接以详细了解多重继承。
3.多级继承:在这种类型的继承中,一个派生类是从另一个派生类创建的。
- CPP
// C++ program to implement
// Multilevel Inheritance
#include<iostream>
using namespace std;
// base class
class Vehicle
{
public:
Vehicle()
{
cout << "This is a Vehicle\n";
}
};
// first sub_class derived from class vehicle
class fourWheeler: public Vehicle
{ public:
fourWheeler()
{
cout << "Objects with 4 wheels are vehicles\n";
}
};
// sub class derived from the derived base class fourWheeler
class Car: public fourWheeler {
public:
Car()
{
cout << "Car has 4 Wheels\n";
}
};
// main function
int main()
{
// Creating object of sub class will
// invoke the constructor of base classes.
Car obj;
return 0;
}
输出
这是一个车辆
有 4 个轮子的对象是车辆
汽车有 4 个轮子
4. 层次继承:在这种类型的继承中,多个子类继承自一个基类。即多个派生类是从单个基类创建的。
- CPP
输出
这是一辆车
这是一辆车
5.混合(虚拟)继承:混合继承是通过组合一种以上的继承来实现的。例如:结合层次继承和多重继承。
下图显示了分层继承和多重继承的组合:
- CPP
// C++ program for Hybrid Inheritance
#include<iostream>
using namespace std;
// base class
class Vehicle
{
public:
Vehicle()
{
cout << "This is a Vehicle\n";
}
};
//base class
class Fare
{
public:
Fare()
{
cout << "Fare of Vehicle\n";
}
};
// first sub class
class Car : public Vehicle
{
};
// second sub class
class Bus : public Vehicle, public Fare
{
};
// main function
int main()
{
// Creating object of sub class will
// invoke the constructor of base class.
Bus obj2;
return 0;
}
输出
这是车辆的车辆
票价
6.混合继承的一种特殊情况:多路径继承:
派生类有两个基类,而这两个基类有一个共同的基类,称为多路径继承。在这种类型的继承中可能会出现歧义。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZrOFV35Q-1643543993815)(http://www.tutorialdost.com/Cpp-Programming-Tutorial/Images/Multipath-Inheritance-Ambiguity-In-Cpp.png)]
考虑以下程序:
- CPP
// C++ program demonstrating ambiguity in Multipath
// Inheritance
#include<iostream>
class ClassA {
public:
int a;
};
class ClassB : public ClassA {
public:
int b;
};
class ClassC : public ClassA {
public:
int c;
};
class ClassD : public ClassB, public ClassC {
public:
int d;
};
int main()
{
ClassD obj;
// obj.a = 10; // Statement 1, Error
// obj.a = 100; // Statement 2, Error
obj.ClassB::a = 10; // Statement 3
obj.ClassC::a = 100; // Statement 4
obj.b = 20;
obj.c = 30;
obj.d = 40;
cout << " a from ClassB : " << obj.ClassB::a;
cout << "\n a from ClassC : " << obj.ClassC::a;
cout << "\n b : " << obj.b;
cout << "\n c : " << obj.c;
cout << "\n d : " << obj.d << '\n';
}
输出:
a 类 B : 10
a 类 C : 100
b : 20
c : 30
d : 40
在上面的例子中,ClassB 和 ClassC 都继承了 ClassA,它们都有一个 ClassA 的副本。但是 ClassD 继承了 ClassB 和 ClassC,因此 ClassD 有两个 ClassA 的副本,一个来自 ClassB,另一个来自 ClassC。
如果我们需要通过ClassD的对象访问ClassA的数据成员a,我们必须指定要访问a的路径,无论是来自ClassB还是ClassC,bco’z编译器无法区分ClassA的两个副本在 D 类。
有两种方法可以避免这种歧义:
使用范围解析运算符避免歧义:
使用范围解析运算符,我们可以手动指定访问数据成员 a 的路径,如上例中的语句 3 和 4 所示。
- CPP
obj.ClassB::a = 10; ``// Statement 3``obj.ClassC::a = 100; ``// Statement 4
注意:仍然,ClassD 中有两个 ClassA 副本。
使用虚拟基类避免歧义:
- CPP
#include<iostream>
class ClassA
{
public:
int a;
};
class ClassB : virtual public ClassA
{
public:
int b;
};
class ClassC : virtual public ClassA
{
public:
int c;
};
class ClassD : public ClassB, public ClassC
{
public:
int d;
};
int main()
{
ClassD obj;
obj.a = 10; // Statement 3
obj.a = 100; // Statement 4
obj.b = 20;
obj.c = 30;
obj.d = 40;
cout << "\n a : " << obj.a;
cout << "\n b : " << obj.b;
cout << "\n c : " << obj.c;
cout << "\n d : " << obj.d << '\n';
}
输出:
a : 100
b : 20
c : 30
d : 40
根据上面的例子,ClassD 只有一个 ClassA 的副本,因此,语句 4 将覆盖在语句 3 中给出的 a 的值。