XLEdoo 2024-11-04 02:07 采纳率: 33.3%
浏览 4
已结题

C++观察者模式由于继承了多个类导致无法收到数据

一个项目中用“观察者”模式进行数据的发送与接收,用户接收数据的TrTableMaster类是一个QTableWidget的列表,同时继承了Observer,Observer的OnReceive,用于接收TrManager::notify发送的数据。
添加观察者:

// 这里的TrManager用的是单例模式, attach 是把Observer*插入到static_gObservers这个vector中。
#define TrAttach(Observer) TrManager::getInstance().attach(Observer)

// TrAttach可以在全局任何地方添加Observer观察者
TrAttach(reinterpret_cast<Observer*>(new TrTableMaster()));
// 这里用(Observer*)进行强制转换效果是一样的。
class Observer
{
public:
    virtual void OnReceive(TrResponseID rId, void* wParam, void* lParam) = 0;
};

这里的TrTableMaster继承了两个类,QTableWidget和Observer,这里的继承顺序不能颠倒,先继承Observer就会报错。

class TrTableMaster : public QTableWidget, public Observer
{
    Q_OBJECT
public:
    explicit TrTableMaster(QWidget* parent = nullptr);
    virtual void OnReceive(TrResponseID rId, void* wParam, void* lParam) override;
};
static std::vector<TrObserver*>                         static_gObservers;

void TrManager::notify(TrResponseID rId, void* wParam, void* lParam)
{
    for (unsigned i = 0; i < static_gObservers.size(); ++i)
    {
        if (nullptr != static_gObservers[i])
        {
            // 打断点已经到了这里,也就是数据已经发送了, 但是TrTableMaster收不到数据,没有调用OnReceive
            static_gObservers[i]->OnReceive(rId, wParam, lParam);
        }
    }
}

现在的问题是,通过上述步骤建立的观察者模式,到static_gObservers[i]->OnReceive(rId, wParam, lParam);这一步后,打断点已经到了OnReceive这里,没问题,但是TrTableMaster的OnReceive并没有调用到函数,也就是没有收到数据,由于项目涉及到QT,TrTableMaster先继承的QTableWidget,再继承Observer,导致添加观察者的时候必须进行类的强制转换,我猜是进行强制转换的时候出的问题(丢失了一些函数?),但是不知道要怎么处理才能达到想要的效果。
也就是说,TrTableMaster这个类必须继承QTableWidget,和Observer,而且顺序不能颠倒,会报错,在这样的情况下,TrTableMaster::OnReceive能收到通知者发送的数据就行。或者说还有其他的解决方案吗?

项目的地址我也贴出来方便指点一下:
https://gitee.com/xledoo/qtrader-master

  • 写回答

5条回答 默认 最新

  • 问答-莫得感情 2024-11-04 02:08
    关注

    ️题主您好️,目前由我为您解答👇🏼
    🧡参考谷歌智能助手,由吾为汝整理提供🧡

    问题的原因在于你使用了reinterpret_cast进行强制类型转换。reinterpret_cast是一种底层的类型转换,它只是简单地重新解释内存中的位模式,而不会进行任何类型检查或调整。当你将new TrTableMaster()的结果reinterpret_castObserver*时,指针的值虽然相同,但编译器会将其视为一个Observer对象的指针,而不是TrTableMaster对象的指针。

    由于多重继承,TrTableMaster对象的内存布局中,Observer部分的起始地址与TrTableMaster对象的起始地址并不相同。当你在TrManager::notify中调用static_gObservers[i]->OnReceive时,由于static_gObservers[i]被视为Observer*,它会调用Observer部分的虚函数表,而不是TrTableMaster的虚函数表。 这会导致调用到错误的OnReceive函数,或者更糟的情况,导致程序崩溃。

    解决方案:

    1. 使用static_castdynamic_cast:

      static_cast是更安全的类型转换方式,它会在编译时进行类型检查。 由于ObserverTrTableMaster的基类,你可以使用static_cast

      TrAttach(static_cast<Observer*>(new TrTableMaster()));
      

      如果基类不是虚继承,static_cast就足够了。 如果是虚继承,为了安全起见,最好使用dynamic_cast

      TrAttach(dynamic_cast<Observer*>(new TrTableMaster()));
      ```  `dynamic_cast`会在运行时检查类型转换的有效性,如果转换失败,会返回`nullptr`。 这可以避免潜在的运行时错误。
      
      
      
    2. 避免reinterpret_cast: 这是最推荐的做法。在C++中,应该尽量避免使用reinterpret_cast,除非你非常清楚自己在做什么。

    3. 使用智能指针: 为了避免内存泄漏,建议使用智能指针:

      #include <memory>
      
      // ...
      
      void TrManager::attach(Observer* observer) {
          static_gObservers.push_back(observer);
      }
      
      // ...
      
      TrAttach(std::make_shared<TrTableMaster>().get());
      

      这里使用std::make_shared创建TrTableMaster的智能指针,然后使用.get()获取原始指针添加到观察者列表中。 智能指针会自动管理TrTableMaster对象的生命周期,避免内存泄漏。 TrManager类可能也需要做相应的修改,例如使用std::shared_ptr<Observer>来存储观察者。

    修改后的代码示例:

    #include <memory>
    
    // ...
    
    std::vector<std::shared_ptr<Observer>> static_gObservers;  // 使用shared_ptr
    
    void TrManager::attach(Observer* observer) {
        static_gObservers.push_back(std::shared_ptr<Observer>(observer)); // 使用shared_ptr
    }
    
    void TrManager::notify(TrResponseID rId, void* wParam, void* lParam){
        for (const auto& observer : static_gObservers) {
            if (observer) {
                observer->OnReceive(rId, wParam, lParam);
            }
        }
    }
    
    // ...
    
    TrAttach(std::make_shared<TrTableMaster>().get());
    

    通过使用static_castdynamic_cast以及智能指针,可以确保类型转换的正确性并避免内存泄漏,从而解决TrTableMaster无法接收数据的问题。 建议优先使用dynamic_cast,因为它更安全,尤其是在处理多重继承和虚继承时。 如果确认没有虚继承,可以使用static_cast。 最重要的是,避免使用reinterpret_cast进行这种类型转换。

    请将以上修改应用到你的项目中,并重新编译运行。 如果还有问题,请提供更多细节,例如TrTableMaster的完整定义以及相关的错误信息。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

问题事件

  • 系统已结题 11月12日
  • 已采纳回答 11月4日
  • 创建了问题 11月4日

悬赏问题

  • ¥15 有两个非常“自以为是”烦人的问题急期待大家解决!
  • ¥30 STM32 INMP441无法读取数据
  • ¥15 R语言绘制密度图,一个密度曲线内fill不同颜色如何实现
  • ¥100 求汇川机器人IRCB300控制器和示教器同版本升级固件文件升级包
  • ¥15 用visualstudio2022创建vue项目后无法启动
  • ¥15 x趋于0时tanx-sinx极限可以拆开算吗
  • ¥15 pyqt信号槽连接写法
  • ¥500 把面具戴到人脸上,请大家贡献智慧,别用大模型回答,大模型的答案没啥用
  • ¥15 任意一个散点图自己下载其js脚本文件并做成独立的案例页面,不要作在线的,要离线状态。
  • ¥15 各位 帮我看看如何写代码,打出来的图形要和如下图呈现的一样,急