首页 > 编程笔记 > C++笔记 阅读:35

C++继承机制详解(单继承和多继承)

C++ 的世界里,继承不仅是一种代码复用的机制,更是一种哲学,一种将现实世界复杂性抽象化的艺术。

正如生物学中的遗传,继承允许我们在软件世界中模拟现实世界的层次结构和关系。这不仅是技术上的实现,更是对现实世界的理解和表达的一种方式。

想象一下,当我们谈论动物园里的动物时,可以抽象出一个“动物”类,这个类包含了所有动物共有的特性和行为,比如呼吸、移动等。然后,让“狮子”“老虎”等具体动物类从“动物”类继承,这样它们就自然地拥有了呼吸和移动的能力。这种设计不仅简化了代码的复杂性,还提高了代码的可读性和可维护性。

现在,让我们从基础开始,探索继承的定义和声明。

继承允许我们定义一个基类(父类)和一个或多个派生类(子类)。派生类继承了基类的公有和受保护成员,同时还可以添加或重写成员函数。例如:
class Animal {
public:
    void breathe() {
        std::cout << "Breathing" << std::endl;
    }
};

class Lion : public Animal {
public:
    void roar() {
        std::cout << "Roaring" << std::endl;
    }
};
在这个例子中,Lion 类继承了 Animal 类,这意味着狮子不仅拥有自己的独特行为——咆哮(roar),还继承了呼吸(breathe)的能力。

C++单继承和多继承

继承有两个基本形式,分别是单继承和多继承:
这两种形式在 C++ 中都是可能的,它们各自代表了不同的设计哲学和应用场景。

在游戏设计过程中,经常会遇到需要表达角色多重身份和能力的情况。以一个典型的角色扮演游戏为例,其中的角色可能需要展示既是某个种族的成员,如精灵或矮人,同时又拥有特定的职业技能,比如法师或战士。

考虑到这种需求,我们可以创建一个基本的角色类 Character,用于定义所有角色共享的属性,如健康值和力量。此外,为了表示某些角色具备施法能力,引入了 SpellCaster 类,专门负责施法相关的功能。如果希望设计一个法师角色 Wizard,这个法师不仅具有基本的角色属性,还能施法,那么单继承和多继承就是我们可以选择的两条路径。

1) 单继承

我们可能会让 Wizard 类直接从 Character 类继承,然后在 Wizard 类中实现施法的功能。

这种做法的局限性在于,施法能力被局限在了 Wizard 类中,如果游戏中出现了其他也能施法的角色,比如一个施法精灵,那么施法的代码就需要在另一个类中重复编写,这显然不利于代码的复用。
class Character {
public:
    void walk() { /* 实现行走 */ }
};
class SpellCaster {
public:
    void castSpell() { /* 实现施法 */ }
};
// 单继承
class Wizard : public Character {
public:
    void castSpell() { /* 在这里实现施法,特定于Wizard */ }
};

2) 多继承

在 C++ 中,我们可以让 Wizard 类同时继承 Character 类和 SpellCaster 类,这样 Wizard 就同时获得了基本角色属性和施法的能力。

多继承使得 Wizard 能够更自然地反映出它同时属于角色和施法者的复杂身份。如果游戏中还有其他能施法的角色,比如一个精灵 Elf,那么 Elf 也可以同时继承 Character 类和 SpellCaster 类,从而避免了代码的重复。
// 多继承
class Wizard : public Character, public SpellCaster {
    // 自动获得了Character和SpellCaster的功能
};
// 另一个角色,能施法的Elf
class Elf : public Character, public SpellCaster {
    // 同样继承了Character和SpellCaster的功能,无须重复实现施法
};
通过使用多继承,我们不仅增强了代码的可复用性,也更贴切地模拟了现实世界中个体可能具有的多重身份和能力,使得游戏角色设计更加灵活和丰富。

3) 多重继承的问题与优势分析

在C++中,多重继承可以带来许多设计上的灵活性,但同时也伴随着一些潜在的复杂性。接下来将详细探讨多重继承所带来的问题和优势。

多重继承的问题:
多重继承的优势:
多重继承在 C++ 中是一个强大的工具,它既带来了诸多优势,也引入了不少挑战。通过合理使用虚拟继承和其他技巧,我们可以最大限度地发挥其优势,同时避免或减轻相关问题。理解这些概念将帮助我们更加精确地掌握面向对象编程中的继承机制,使我们能够在实际项目中更有效地运用这些技术。

C++继承中的权限关系

在 C++ 的继承中,理解权限关系是至关重要的。这不仅关乎代码的可访问性,更关乎如何在设计中恰当地表达意图和确保数据的封装。

继承中的权限关系,就像是在不同的社会角色之间设定边界,确保每个角色都能在适当的范围内行动,既保持了秩序,又给予了必要的自由度。

让我们回顾一下相关知识点,C++ 中类成员可以被声明为 public、protected 或 private:
当一个类从另一个类继承时,基类成员的访问权限会受到派生方式的影响。C++ 支持 3 种继承方式:
举个简单的例子,想象一个王国中的 3 种不同的领地分配方式,这些方式决定了领地的使用权限和规则:
class Kingdom {
public:
    void publicGarden() { /* ... */ }  // 公园,任何人可见
protected:
    int privateGardenArea = 300;  // 保护领地面积
private:
    string secretDocuments = "Classified";  // 秘密文件
};

// 公有继承
class Prince : public Kingdom {
    // Prince可以访问publicGarden()和privateGardenArea,但不能访问secretDocuments
};

// 受保护继承
class Duke : protected Kingdom {
    // Duke可以访问publicGarden()和privateGardenArea,但这些在Duke中都成了protected
    // secretDocuments仍然不可访问
};

// 私有继承
class Earl : private Kingdom {
    // Earl可以访问publicGarden()和privateGardenArea,但这些在Earl中都成了private
    // secretDocuments仍然不可访问
};
通过这个示例,我们可以更直观地理解不同继承方式对成员访问权限的影响。在设计类的层次结构时,选择合适的继承方式有助于我们更好地封装数据,并确保只有恰当的类能访问特定的资源,从而维持代码的秩序和清晰度。

相关文章