【QT/C++】实例理解类间的六大关系之泛化关系(Generalization)

【QT/C++】实例理解类间的六大关系之泛化关系(Generalization)

在前面章节一文完美概括UML类图及其符号(超详细介绍)中已经对泛化关系的概念进行了总结,本文我将用实际案例来进一步理解泛化关系,以便应对未来的考试或面试!

(关注不迷路哈!!!)



前言 📊

泛化关系

// 基类(抽象) // 数据库Database
class Database {
public:
    virtual void connect() = 0;  // 纯虚函数 // 连接数据库
};

// 派生类(实现)// MySQL是数据库Database
class MySQL : public Database {
public:
    void connect() override {
        cout << "Connecting to MySQL..." << endl;
    }
};

// 派生类(实现)// PostgreSQL是数据库Database
class PostgreSQL: public Database {
public:
    void connect() override {
        cout << "Connecting to PostgreSQL..." << endl;
    }
};

一、核心概念 🔍

父类定义通用接口,子类实现具体行为

特性说明
本质继承机制(父子类之间的"is-a"关系)
设计原则子类继承父类属性和方法,并可扩展新功能
多态基础父类指针/引用可指向子类对象,实现运行时绑定
UML表示子类 --▷ 父类(实线 + 空心三角箭头)
代码关键字C++ 代码: class Sub : public Base

二、继承类型对比表 🔒

继承方式基类成员访问权限变化适用场景耦合度
public基类public→子类public,基类protected→子类protected严格"is-a"关系(如Dog is Animal
protected基类public/protected→子类protected限制外部访问,保留内部复用
private基类public/protected→子类private“以…方式实现”(非"is-a")

📝 关键规则:基类private成员在子类中始终不可见(存在但无法访问)。


三、代码示例与多态实现 ⚙️

1. 代码实现逻辑

代码实现逻辑

2. 完整代码展示

#include <iostream>
using namespace std;

// 基类(抽象接口)
class Database {
public:
    virtual void connect() = 0;  // 纯虚函数,父类定义 virtual 接口方法
    virtual ~Database() = default; // ✅ 虚析构函数(避免资源泄漏),基类析构函数必须为 virtual,否则子类资源泄漏
};

// 子类实现
class MySQL : public Database {  // public继承
public:
    void connect() override { // 子类用 override 显式重写(C++11及以上)
        cout << "MySQL: 连接成功" << endl;
    }
};

class PostgreSQL : public Database {
public:
    void connect() override { // 子类用 override 显式重写(C++11及以上)
        cout << "PostgreSQL: 连接成功" << endl;
    }
};

// 多态调用
void connectDatabase(Database* db) {
    db->connect();  // 运行时绑定具体实现
}

int main() {
    Database* db1 = new MySQL(); // 父类指针指向子类对象
    connectDatabase(db1);  // 输出: MySQL: 连接成功
    
    Database* db2 = new PostgreSQL(); // 父类指针指向子类对象
    connectDatabase(db2);  // 输出: PostgreSQL: 连接成功
    
    delete db1;
    delete db2;
    return 0;
}

3. 代码交互流程

代码交互流程


四、泛化关系的优缺点与最佳实践 💎

维度优点缺点最佳实践
代码复用减少冗余代码,子类复用父类逻辑父类变更可能破坏子类功能(高耦合)优先用组合(has-a)替代继承
多态支持运行时动态绑定,扩展性强虚函数调用有性能开销基类析构函数必须为virtual
设计易于表达层次关系(如动物→猫/狗)多重继承导致菱形问题(需虚继承解决)避免超过3层继承深度

泛化关系在设计上应遵循里氏替换原则:子类必须完全兼容父类行为,父类出现处可替换为子类。

💡 组合优先示例

class Engine {};   // 引擎类
class Car {
private:
     Engine engine; // 组合而非继承(Car has an Engine)
};

// 而非:
class Car : public Engine {};  // 错误继承

五、面试常见问题及回答 🚀

1. 问:何时使用继承?何时用组合?✅

  • 继承
    1. 当需要表达严格的"is-a"关系时(如Circle is a Shape
    2. 需要实现运行多态时
    3. 符合里氏替换原则的场景

  • 组合:当需要"has-a"关系或避免耦合时(如Car包含Engine

2. 问:为什么父类需要虚析构函数(virtual)?✅

  • 避免内存泄漏
    Base* p = new Derived();
      // 若基类析构非虚,仅调用Base::~Base() → 内存泄漏
    delete p; // 若~Base()非虚,仅调用~Base()导致Derived部分泄漏
    

3. 问:多重继承会导致什么问题,如何解决?✅

    1. 菱形继承问题(需虚继承解决)
      class A {};
      class B : virtual public A {};
      class C : virtual public A {};
      class D : public B, public C {};
      
    1. 增加复杂度,易引发命名冲突
    1. 推荐用接口继承+组合替代

4. 问:如何设计图形系统且支持多态绘制?✅

  • 具体实现代码示例
    class Shape {
    public:
        virtual double area() const = 0;
        virtual void draw() const = 0;
        virtual ~Shape() = default;
    };
    
    class Circle : public Shape {
        double radius;
    public:
        double area() const override { 
            return 3.14 * radius * radius; 
        }
        void draw() const override {
            cout << "○" << endl;
        }
    };
    

设计图形系统


总结 🛠️

  • 继承的优缺点
    优点是可以实现代码重用和多态;
    缺点是可能增加类之间的耦合(降低通用性),过度使用继承可能导致层次过深、结构复杂。
  • 组合优先于继承
    在可能的情况下,优先使用组合而不是继承,因为组合的耦合度较低。

通过合理使用泛化关系,可构建高扩展性的面向对象系统,但需警惕过度继承导致的维护复杂性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QT 小鲜肉

感谢您的鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值