C++ 子类继承
继承是 OOP 的一个特性,其中一个类获取另一个类的属性和行为。 继承另一个类的类称为子类,而其属性被继承的类称为基类。
本文将讨论继承类时出现的钻石问题。
C++ 子类继承
继承是一种让一个类继承另一个类的属性的功能。 继承另一个类的属性的类称为子类或派生类,而继承其属性的类称为基类或父类。
现实世界中继承的一个例子是孩子继承其父母的财产。
C++中的继承主要有五种类型,即单继承、多重继承、多级继承、层次继承和混合继承。
- 单一继承 - 在这种类型的继承中,单个子类继承自单个基类。
- 多重继承 - 在这种类型的继承中,子类是从多个基类继承的。
- 分层继承 - 在这种类型的继承中,多个子类从同一个基类继承。
- 多级继承 - 在这种类型的继承中,一个类被另一个类继承,而另一个类又被其他一些类继承。 比如我们有A、B、C三个类,其中C类被B类继承,B类被A类继承。
- 混合继承 - 在这种类型的继承中,组合了多种类型的继承。
我们首先讨论C++中的多重继承和层次继承。
C++ 子类的多重继承
如前所述,在多重继承中,子类被多个其他基类继承。 让我们举个例子来详细理解多重继承。
#include<iostream>
using namespace std;
class Base1
{
public:
Base1() {
cout << "Base class 1" << endl;
}
};
class Base2
{
public:
Base2() {
cout << "Base class 2" << endl;
}
};
class Derived: public Base2, public Base1
{
public:
Derived() {
cout << "Derived class" << endl;
}
};
int main()
{
Derived d;
return 0;
}
输出:
Base class 2
Base class 1
Derived class
上面的代码有两个基类Base1和Base2,Derived类继承自这两个基类。 但是,您必须注意基类构造函数的调用顺序。
首先,调用 Base2 类构造函数,因为 Derived 类首先继承它,然后调用 Base1 构造函数。
C++中子类的层次继承
如前所述,在层次继承中,多个子类是从单个基类继承的。 我们通过一个例子来详细了解一下层次继承。
#include<iostream>
using namespace std;
class Base
{
public:
Base() {
cout << "Base class" << endl;
}
};
class Derived1: public Base
{
public:
Derived1() {
cout << "Derived class 1" << endl;
}
};
class Derived2: public Base
{
public:
Derived2() {
cout << "Derived class 2" << endl;
}
};
int main()
{
Derived1 d1;
Derived2 d2;
return 0;
}
输出:
Base class
Derived class 1
Base class
Derived class 2
上面的代码示例有三个类,其中 Derived1 和 Derived2 类继承自公共 Base 类。
C++ 继承中的钻石问题
当我们将层次继承和多重继承结合起来时,就会出现钻石问题。 之所以这样称呼这个问题,是因为类在相互继承的同时形成了菱形。
假设我们有一个场景,有四个类 A、B、C 和 D。
A 类充当基类。 B类被A类继承。 C类也被A类继承。 D 类被 B 类和 C 类继承。
现在让我们通过代码看看出现的问题。
#include<iostream>
using namespace std;
class A
{
public:
A() {
cout << "Constructor A here!" << endl;
}
};
class B: public A
{
public:
B() {
cout << "Constructor B here!" << endl;
}
};
class C: public A
{
public:
C() {
cout << "Constructor C here!" << endl;
}
};
class D: public B, public C
{
public:
D(){
cout<<"Constructor D here!"<< endl;
}
};
int main()
{
D d;
return 0;
}
输出:
Constructor A here!
Constructor B here!
Constructor A here!
Constructor C here!
Constructor D here!
上面的输出显示,类 A 的构造函数被调用了两次,因为类 D 获得了类 A 的两个副本。一个通过继承类 B,另一个通过继承类 C。
这会产生歧义,在 C++ 中称为钻石问题。
这个问题主要发生在一个类继承多个基类时,这些基类继承自一个公共基类。
不过,这个问题可以使用C++中的virtual关键字来解决。 我们让双父类从同一个基类继承为虚拟类,这样子类就不会获得公共祖父母类的两个副本。
因此,我们在代码示例中创建 B 类和 C 类虚拟类。
让我们借助代码示例来了解此问题的解决方案。
#include<iostream>
using namespace std;
class A
{
public:
A() {
cout << "Constructor A here!" << endl;
}
};
class B: virtual public A
{
public:
B() {
cout << "Constructor B here!" << endl;
}
};
class C: virtual public A
{
public:
C() {
cout << "Constructor C here!" << endl;
}
};
class D: public B, public C
{
public:
D(){
cout<<"Constructor D here!"<< endl;
}
};
int main()
{
D d;
return 0;
}
输出:
Constructor A here!
Constructor B here!
Constructor C here!
Constructor D here!
因此,如上面的输出所示,现在我们只获得了类 A 的构造函数的一份副本。 virtual 关键字告诉编译器,类 B 和类 C 都继承自同一个基类 A; 因此,它应该只被调用一次。
总结
在本文中,我们简要讨论了继承和继承的类型。 然而,我们主要详细讨论了两种类型的继承——多重继承和层次继承,这引起了继承中的钻石问题。
当一个类从多个基类继承,而该基类又从单个基类继承时,就会出现菱形问题。 不过这个问题在C++中使用virtual关键字就解决了。
相关文章
Arduino 复位
发布时间:2024/03/13 浏览次数:315 分类:C++
-
可以通过使用复位按钮,Softwarereset 库和 Adafruit SleepyDog 库来复位 Arduino。
Arduino 的字符转换为整型
发布时间:2024/03/13 浏览次数:181 分类:C++
-
可以使用简单的方法 toInt()函数和 Serial.parseInt()函数将 char 转换为 int。
Arduino 串口打印多个变量
发布时间:2024/03/13 浏览次数:381 分类:C++
-
可以使用 Serial.print()和 Serial.println()函数在串口监视器上显示变量值。