关于继承的两个原则

博客介绍了面向对象设计的两个原则。类型一致原则要求子类与父类类型一致,子类不变式更严格,且满足方法名、签名相同,前置条件宽松,后续条件严格。闭合行为原则指执行方法时,类行为应遵守不变式,子类需考虑父类行为对自身不变式的影响并覆盖违法方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1          类型一致原则如果ST的一个子类型,那么S必须与T保持一致。如果在任何需要类型T的对象出现、且在任何存取方法执行时都需要保持正确性的场合,都能够使用类型S的对象,那么就可以说ST是同一类型的。

例如,圆是椭圆的一个子类型,那么圆类型的对象同时也是椭圆类型的对象,任何可以将椭圆类型的对象作为参数的方法,都应该可以接受圆类型的对象作为参数。

在一个良好的面向对象设计中,类/子类的层次结构应该是和类型/子类型的层次结构保持一致的。也就是说,应当把每个子类设计成每个父类的子类型,同时遵守类型一致的原则。

为了保证子类型的一致性,首先需要保证子类的不变式的要求比父类的不变式的更加严格或者至少同样的严格。比如说Rectangle类具有不变式w1 = w2 and h1 = h2,那么Square类作为Rectangle类的子类,它的不变式w1 = w2 and h1 = h2 and w1 = h1就比父类的不变式要求更加严格。将这个要求分解开来,也就是要求以下三个约束条件:

1)    父类的每个方法都出现在子类当中,并且拥有相同的方法名、签名;

2)    子类中的方法的前置条件的要求应当比父类中对应方法的要求宽松或者相同(逆变量原则);

3)    子类中的方法的后续条件的要求应当比父类中对应方法的要求更严格或者相同(协变量原则)。

 

2          闭合行为原则:在基于类型/子类型的继承层次中,当执行任何方法(getter方法和setter方法)时,每个类的行为(包括继承的父类的方法)都应当遵守类的不变式。

    比如,TrianglePolygon的子类,Polygon有如下两个方法:moveaddVertex。对于一个Triangle的对象tri,作用move方法,将使得tri的位置发生变化。Tri在移动之后仍然保持为一个三角形(也就是保持为Triangle类型)。Polygon的行为将对象留在了同Triangle的状态空间一样的状态空间之中,这种情况下,可以说子类Triangle在父类的方法move所定义的行为下是闭合的。但是addVertex方法所定义的行为会导致tri增加一个顶点,使得tri不再是Triangle类型的对象而成为了一个四边形。这种情况下,可以说子类Triangle在父类的方法addVertex所定义的行为下不是闭合的。

    通常,父类行为下的子类的闭包不会自动产生,程序员应当在设计子类的时候对此加以考虑,对于父类中会违法子类不变式的方法加以覆盖。

<think>在C++中,多继承允许一个类从多个基类继承。Qt框架中,有时我们需要继承两个类,例如同时继承QWidget和某个接口类。但是,Qt框架中有一个特殊的点:QObject继承体系支持普通的多继承(因为元对象系统需要唯一性)。所以,如果要继承两个类都是QObject的派生类(如QWidget和QMainWindow等),则能直接使用多继承。但可以使用以下方法:1.使用单一继承和组合:这是推荐的做法,特别是当其中一个类是必须作为基类时。2.如果两个类都是QObject派生类,可以考虑使用多重继承,但必须确保只有一个QObject子类。具体来说:-如果其中一个类是QObject的直接或间接子类,那么就可以安全地多继承。-如果两个类都是QObject的子类,那么通常能这样做,因为QObject在继承树中只能出现一次(由于元对象系统的要求)。在这种情况下,我们可以使用接口类(即只有纯虚函数的抽象类)并确保这个接口类继承QObject。但是,有一种例外情况:Qt提供了一个叫做“接口类”的概念,通常以`Q_DECLARE_INTERFACE`来声明。这样的接口类是从QObject继承的,所以可以多继承。另外,Qt中的一些类是使用虚继承的(如QDockWidget继承自QWidget和QDockWidgetInterface等),但这是内部实现,用户代码中常见。在Qt中,我们经常需要继承QObject以及另一个类(例如,一个自定义的接口),这时可以使用多继承,但必须遵循:-最多只有一个类是非虚继承的QObject子类(即通常的QObject派生类)。-其他基类要么是纯虚类(没有QObject)要么是含QObject的普通类。例如,我们想同时继承一个QWidget和一个自定义的接口类`MyInterface`:```cpp//自定义接口,继承QObjectclassMyInterface{public:virtualvoiddoSomething()=0;virtual~MyInterface(){}};classMyWidget:publicQWidget,publicMyInterface{Q_OBJECT//这里Q_OBJECT只能放在一个基类中,即只有一个QObject子类基类public:explicitMyWidget(QWidget*parent=nullptr);voiddoSomething()override;};```注意:这里`MyInterface`能是QObject的派生类,否则会导致多个QObject子类基类,从而引发错误。但是,如果你需要继承两个QObject派生类,例如同时继承QWidget和QAbstractItemModel(这种情况很罕见),那么通常能直接多重继承。此时,你需要重新设计,比如使用组合或者使用一个类继承一个,另一个作为成员。然而,有一种情况例外:如果其中一个类是通过虚继承的方式继承QObject?但Qt并支持虚继承QObject(因为元对象系统需要明确的父子关系),所以要尝试这样做。另外一种常见情况:我们经常需要从QObject和一个线程类(如QRunnable)继承,但QRunnable是QObject,所以可以直接多重继承。例如:```cppclassMyRunnable:publicQObject,publicQRunnable{Q_OBJECTpublic:MyRunnable();voidrun()override;};```这没有问题,因为QRunnable是QObject的派生类。所以,总结如下:-如果需要继承两个类中,只有一个类直接或间接继承自QObject,那么可以直接多重继承,并在类声明中加入Q_OBJECT宏(注意:这个宏必须放在类声明的开始,通常放在第一个private或public之后,但必须在类体中)。-如果两个类都是QObject的派生类,则能多重继承(除非其中一个类是以虚继承的方式继承QObject,但Qt推荐,也支持,所以要这样做)。下面给出一个代码示例,展示如何同时继承一个QWidget和一个非QObject的接口类:```cpp#include<QWidget>//非QObject的接口classPrintable{public:virtualvoidprint()const=0;virtual~Printable()=default;};classMyWidget:publicQWidget,publicPrintable{Q_OBJECTpublic:explicitMyWidget(QWidget*parent=nullptr):QWidget(parent){}voidprint()constoverride{qDebug()<< "PrintingMyWidget...";}};```在这个例子中,MyWidget同时继承了QWidget和Printable接口。由于Printable没有继承QObject,所以这是安全的。然而,如果Printable是一个QObject(比如它使用了Q_OBJECT宏),那么就会造成多个QObject基类,这将导致编译错误(元对象系统冲突)。因此,在Qt中使用多重继承时,必须谨慎处理基类是否是QObject派生类。相关问题:1.在Qt中,如何处理需要多个QObject派生类的功能(即必须从两个QObject派生类继承)?2.Qt中的Q_DECLARE_INTERFACE宏有什么作用?如何使用?3.在什么情况下需要虚继承QObject?Qt是否支持?4.使用组合代替继承来实现多个QObject派生类的功能,请给出示例。</think> ### C++ Qt 如何继承两个类 在 Qt 框架中实现多重继承时,需要特殊处理 **QObject** 继承体系(Qt 的元对象系统对多重继承有特殊要求)。以下是解决方案: #### 正确实现方法 ```cpp #include <QObject> #include <QWidget> // 第一个基类(QObject 的直接派生) class Sensor : public QObject { Q_OBJECT public: explicit Sensor(QObject* parent = nullptr) : QObject(parent) {} void readData() { /* ... */ } }; // 第二个基类(无 QObject 继承) class Serializable { public: virtual QString serialize() const = 0; virtual ~Serializable() = default; }; // 正确多重继承 (只继承一个 QObject) class Device : public Sensor, public Serializable { Q_OBJECT // ✔️ 只在一个 QObject 派生链上 public: explicit Device(QObject* parent = nullptr) : Sensor(parent) {} QString serialize() const override { return "DeviceData"; } }; // 示例:同时继承控件和接口 class SmartThermostat : public QWidget, public Serializable { Q_OBJECT // 直接继承 QWidget(QObject 派生类) public: QString serialize() const override { return "ThermostatData"; } }; ``` #### 错误示范(会导致元对象系统崩溃) ```cpp // ❌ 危险! 多重继承多个 QObject 子类 class InvalidDevice : public Sensor, public QWidget { // 无法正确处理元对象系统 // 编译错误或运行时崩溃 }; ``` ### 关键原则 1. **唯一 QObject 基类原则** - 派生类只能继承 **一个** QObject 的直接或间接子类 - 其他基类必须是普通 C++ 类(无 QObject 宏) 2. **接口分离** - 需要多重功能时,使用非 QObject 的纯虚接口类 - Qt 的标准做法示例: ```cpp class Drawable { // 非 QObject 接口 public: virtual void draw() = 0; }; ``` 3. **Qt 内置方案** 对于需要多重继承的特殊场景: ```cpp // 使用组合代替继承 class CustomControl : public QWidget { Q_OBJECT public: Serializer* serializer() { return &m_serializer; } private: JsonSerializer m_serializer; // 组合功能类 }; ``` 4. **虚继承限制** Qt **支持**虚继承 QObject: ```cpp class A : public virtual QObject {}; // ❌ 禁止! ``` ### 处理冲突的方法 当必须继承两个 QObject 派生类时: ```cpp // 方法 1: 使用中间层封装 class HardwareInterface : public QObject { Q_OBJECT // 硬件控制功能... }; class DeviceDriver : public HardwareInterface { // 单一 QObject 继承链 // 添加软件功能 }; // 方法 2: 包含另一个 QObject 作为成员 class AdvancedDevice : public HardwareInterface { Q_OBJECT public: NetworkManager* network() { return m_network; } private: NetworkManager* m_network; // 另一个QObject子类 }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值