文章目录
前言
在 C++ 中,继承是面向对象编程(OOP)的一个重要特性,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。通过继承,可以实现代码重用,减少重复代码,并支持多态性。
一、访问修饰符
- Public:基类的公有成员在派生类中仍然是公有的,保护成员在派生类中仍然是保护的。
- Protected:基类的公有和保护成员在派生类中都成为保护成员。
- Private:基类的公有和保护成员在派生类中都成为私有成员。
二、使用
1.单继承
代码如下(示例):
class Base {
public:
void publicMethod() { /* ... */ }
protected:
int protectedVar;
};
// :public Base 就是说以pubiic的方式继承
class Derived : public Base {
public:
void anotherMethod() {
// 可以访问 publicMethod 和 protectedVar
}
};
2.多继承
代码如下(示例):
class Base1 {
public:
void methodOfBase1() { /* ... */ }
};
class Base2 {
public:
void methodOfBase2() { /* ... */ }
};
//这里就是多继承的写法
class Derived : public Base1, public Base2 {
public:
void derivedMethod() {
methodOfBase1();
methodOfBase2();
}
};
C++ 支持一个类从多个基类继承。
3.虚继承
代码如下(示例):
class Base {
public:
virtual ~Base() {}
};
//增加一个virtual关键字
class Derived1 : virtual public Base {};
class Derived2 : virtual public Base {};
class MultiDerived : public Derived1, public Derived2 {
public:
MultiDerived() {}
};
当存在多重继承且多个基类拥有共同的基类时,可能会导致二义性和重复继承的问题。虚拟继承用于解决这些问题。
4.纯虚函数
class Base {
public:
virtual void pure_virtual_function() = 0; // 纯虚函数
};
class Derived : public Base {
public:
void pure_virtual_function() override {
// 提供具体的实现
}
};
包含纯虚函数的类被称为抽象类,而不能创建抽象类的对象,只能创建其非抽象派生类的对象。
定义纯虚函数的主要目的是为派生类提供一个统一的接口,同时允许不同的派生类根据自己的特点来实现该接口。继承了抽象类的对象想要实例化,必须重写纯虚函数,这样可以确保所有派生类都必须实现这个函数,从而保证了一定程度上的代码一致性。
5.析构函数与继承
代码如下(示例):
class Base {
public:
virtual ~Base() {}
};
//增加一个virtual关键字
class Derived1 : virtual public Base {};
class Derived2 : virtual public Base {};
class MultiDerived : public Derived1, public Derived2 {
public:
MultiDerived() {}
};
在继承体系中,如果基类定义了析构函数,那么通常应该将其声明为虚函数(virtual),以确保当通过基类指针删除派生类对象时,派生类的析构函数也会被调用。
三.容易出现的问题
1.菱形继承问题
也称为死亡钻石,是面向对象编程中多继承带来的一个经典问题。这个问题得名于继承结构图的形状,该形状类似于菱形或钻石。
在这个例子中,D 类同时继承自 B 和 C,而 B 和 C 都继承自 A。如果 A 中有一个方法或属性,那么在 D 中访问这个成员时就可能出现二义性,因为 D 可以通过 B 或 C 继承得到两个版本的 A 的成员。这在某些语言中会导致编译错误,或者如果不处理好,可能会导致程序行为不符合预期。
可以通过使用虚继承(virtual inheritance)来解决菱形继承的问题。虚继承确保了即使一个类从多个路径继承同一个基类,它也只会拥有该基类的一个实例,从而避免了二义性和重复构造基类的问题。
2.二义性
当一个派生类从多个基类继承,并且这些基类有同名成员时,访问该成员会导致编译器无法确定应该使用哪个基类的成员,从而产生二义性。
解决方法包括显式地指定要使用的基类成员,例如 BaseClass::member,或者通过虚继承来确保只有一个基类副本。
3.内存布局复杂性
多继承和虚继承会增加对象的内存布局复杂度,可能引入额外的指针(如虚表指针),并导致对象大小增加。
4.代码可读性和维护性下降
随着继承层次的加深和复杂性的增加,代码变得更加难以理解和维护。过度依赖继承而非组合,可能会使设计变得脆弱。
5.紧耦合
继承建立了一种强耦合关系,基类的变化可能会影响所有派生类。为了降低这种风险,可以考虑使用接口或纯虚函数类来定义行为契约,而不是具体的实现。
6.使用推荐
为了避免这些问题,开发者应该遵循“组合优于继承”的原则(不知道组合的话可以了解一下),在适当的情况下优先选择对象组合而非继承。此外,使用单一责任原则(SRP)来限制每个类的责任范围,也可以帮助简化继承体系,提高代码的可维护性和灵活性。
四.优缺点
1.C++ 继承的优点
- 代码重用:
继承允许创建新的类(派生类),这些类可以继承现有类(基类)的属性和方法,从而减少了重复代码的数量,并提高了代码的可维护性。 - 扩展性:
派生类可以在不修改基类的情况下增加新的功能或覆盖已有功能。这有助于软件的演进和发展,同时保持了旧版本的兼容性。 - 多态性:
通过虚函数和接口,继承支持运行时多态性,使得程序能够在不知道具体类型的情况下调用对象的方法。这增加了灵活性,简化了复杂系统的设计。 - 层次结构建模:
继承可以帮助构建清晰的对象层次结构,模拟现实世界中的“is-a”关系,比如“狗是一种动物”,这种关系在某些应用领域(如图形界面、游戏开发等)特别有用。 - 抽象与接口分离:
使用纯虚函数或抽象类,可以让基类定义一个接口而不提供实现,迫使派生类去实现这些接口,有助于建立良好的设计模式。
2.C++ 继承的缺点
- 紧耦合:
继承创建了基类和派生类之间的紧密依赖关系,基类的变化可能会影响所有派生类,这可能会导致维护上的困难。 - 脆弱的基类问题:
基类的一个小改动可能导致所有派生类的行为发生变化,甚至破坏它们的功能。这被称为“脆弱基类问题”。 - 二义性和菱形继承问题:
在多重继承中,可能会出现二义性或菱形继承问题,这些问题需要额外的机制(如虚拟继承)来解决,但也会增加代码的复杂度。 - 构造和析构顺序的问题:
构造函数和析构函数的执行顺序是固定的,如果处理不当,可能会导致未初始化成员变量或其他问题。 - 性能开销:
虚函数调用引入了一定程度的间接性,可能会带来轻微的性能损失。此外,复杂的继承层次可能会增加对象的大小和内存布局的复杂性。 - 设计复杂度:
过度使用继承会导致类的设计变得复杂,难以理解和维护。尤其是在大型项目中,深且宽的继承树可能会使代码库难以管理。 - 组合优于继承:
在很多情况下,使用对象组合(Composition)而不是继承能够更好地解决问题,因为组合提供了更大的灵活性,降低了模块间的耦合度。
总结
继承是一个强大的工具,但它并非总是最优的选择。在C++编程中,应该谨慎地评估是否使用继承以及如何使用它。遵循“组合优于继承”的原则,在适当的情况下优先选择组合,可以使代码更加灵活、易于理解和维护。同时,利用继承的优势,如代码重用和多态性,可以有效地提高软件的质量和效率。
标签:继承,基类,C++,public,Base,派生类,class From: https://blog.csdn.net/qq_62718148/article/details/144861801