基类子类在Qt信号量机制下的思考

背景知识:

基类 superClass

class superClass
{
public:
    superClass(){ std::cout << "superClass()" << std::endl; }
    virtual ~superClass(){}

    virtual void print()
    {
        std::cout << "superclass print " << std::endl;
    }
};

子类subClass

class subClass : public superClass
{
public:
    subClass():superClass(){ std::cout << "subClass()" << std::endl; }
    ~subClass()
    {
        std::cout << "~subClass" << std::endl;
    }

    void print()
    {
        std::cout << "subclass print " << std::endl;
    }

    void test()
    {
        std::cout << "test" << std::endl;
    }
};

生产者 生产子类,然后通过信号传递出去。消费者订阅生产者的信号,然后消费子类。


mysignal(cn)


单,多线程


会被复制


mysignal(cn &)


单线程


不会被复制


mysignal(const cn &)


单,多线程


会被复制

解决方案1:

生产者和消费者在同一线程,那么只能按照生产一个消费一个的模式工作。

Signal的定义可以为:

signals:
void mysignal(superClass &);

该签名只能用于单线程模式。

多线程模式下:

signals:
void mysignal(superClass);

该模式下,消费者端代码如下:

public slots:
    void consume(superClass sc)
    {
        subClass & sub = (subClass &)(sc);
        sub.print();
    }

或者

signals:
    void mysignal(const superclass &);

该模式下,消费者端代码如下:

public slots:
    void consume(const superClass & sc)
    {
        superClass temp = sc;
        subClass & sub = (subClass &)(temp);
        sub.print();
}

此时,sub调用的是父类的print方法。

解决方案二:

在子类中添加一个通过父类来构造子类的构造方法:

class subClass : public superClass
{
public:
    subClass():superClass(){ std::cout << "subClass()" << std::endl; }

    subClass(const superClass & s):subClass()
    {
        std::cout << "construct subClass from superClass()" << std::endl;
    }

    ~subClass()
    {
        std::cout << "~subClass" << std::endl;
    }

    void print() override
    {
        std::cout << "subClass" << a << std::endl;
    }

    void setA(int v_) { a = v_; }

    void test()
    {
        std::cout << "test" << std::endl;
    }

private:
    int a = 0;
};

生产者如下生产:

    void produce()
    {
        subClass sub;
        sub.setA(100);
        emit mysignal(sub);
    }

消费者如下消费:

    void consume(const superClass & sc)
    {
        subClass sub = sc;
        sub.print();
    }

但此时subClass中的a信息已经丢失。

解决方案三:

改用指针,方法签名为:

signals:
void mysignal(superClass *);

生产者如下生产:

    void produce()
    {
        subClass * sub = new subClass();
        sub->setA(100);
        emit mysignal(sub);
    }

消费者如下消费:

    void consume(superClass * sc)
    {
        subClass * sub = dynamic_cast<subClass *>(sc);
        sub->print();
    }

但是在有多个消费者的时候,

    connect(&p, SIGNAL(mysignal(superClass *)), &c, SLOT(consume(superClass *)));
    connect(&p, SIGNAL(mysignal(superClass *)), &c2, SLOT(consume(superClass *)));

没有消费者适合删除指针。必然导致内存泄漏。一旦有消费者删除了指针,则其他未消费的消费者将崩溃。这样的设计有一个隐患,一个消费者的恶意代码可以搞垮其他消费者。

解决方案四:

放弃QT 信号机制,自己实现ResultHandler,所有的消费者通过register方式将直接的消费方式注册到统一的地方,由一个地方统一调用。这样的话,各个线程的方法会排队执行,失去多线程的意义。

综上,放弃父类子类设计方式。由统一一个类来实现所有功能。但是传递对象有效率问题,因为有多少个消费者,就会复制多少份对象。

爱恨难取舍。

时间: 2024-10-10 10:00:21

基类子类在Qt信号量机制下的思考的相关文章

Qt 事件处理机制

Qt 事件处理机制 因为这篇文章写得特别好,将Qt的事件处理机制能够阐述的清晰有条理,并且便于学习.于是就装载过来了(本文做了排版,并删减了一些冗余的东西,希望原主勿怪),以供学习之用. 简介 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent.Qt是以事件驱动UI工具集.Signals/Slots在多线程中的实现也是依赖于Qt的事件处理机制.在Qt中,事件被封装成一个个对象,所有的事件都继承抽象基类QEvent. Qt事件处理机制 产生事件:输入设备,键盘鼠标等.keyPr

小酌重构系列[11]&mdash;&mdash;提取基类、提取子类、合并子类

概述 继承是面向对象中的一个概念,在小酌重构系列[7]--使用委派代替继承这篇文章中,我"父子关系"描述了继承,这是一种比较片面的说法.后来我又在UML类图的6大关系,描述了继承是一种"is a kind of"关系,它更偏向于概念层次,这种解释更契合继承的本质.本篇要讲的3个重构策略提取基类.提取子类.合并子类都是和继承相关的,如果大家对继承的理解已经足够深刻了,这3个策略用起来应该会得心应手. 提取基类 定义:如果有超过一个类有相似的功能,应该提取出一个基类,并

关于C#基类和子类函数调用问题

c#基类子类的函数调用关系,代码说明newkeyword后面的类中的函数为对象调用的函数,当然必需要有virtual和override,继承就相当于包括了基类的函数,子类对象调用时基类的函数相当于就在子类其中一样.(必需要有virtual和override此代码才成立),问题是C#基础问题但非常easy搞错,代码片在unity3d中測试,需要UnityEngine.dll. using UnityEngine; using System.Collections; public class New

构造函数为什么不能为虚函数 &amp;amp; 基类的析构函数为什么要为虚函数

一.构造函数为什么不能为虚函数 1. 从存储空间角度,虚函数相应一个指向vtable虚函数表的指针,这大家都知道,但是这个指向vtable的指针事实上是存储在对象的内存空间的.问题出来了,假设构造函数是虚的,就须要通过 vtable来调用,但是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数. 2. 从使用角度,虚函数主要用于在信息不全的情况下,能使重载的函数得到相应的调用.构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀.所以构造函数没有必要是虚

构造函数为什么不能为虚函数 &amp; 基类的析构函数为什么要为虚函数

一.构造函数为什么不能为虚函数 1. 从存储空间角度,虚函数对应一个指向vtable虚函数表的指针,这大家都知道,可是这个指向vtable的指针其实是存储在对象的内存空间的.问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,怎么找vtable呢?所以构造函数不能是虚函数. 2. 从使用角度,虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用.构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀.所以构造函数没有必要是虚函

为什么基类的析构函数是虚函数?

1.第一段代码 #include<iostream> using namespace std; class ClxBase{ public: ClxBase() {}; ~ClxBase() { cout << "Output from the destructor of class ClxBase!" << endl; }; void DoSomething() { cout << "Do something in class

C++ Primer 学习笔记_66_面向对象编程 -定义基类跟派生类[续]

面向对象编程 --定义基类和派生类[续] 四.virtual与其他成员函数 C++中的函数调用默认不使用动态绑定.要触发动态绑定,必须满足两个条件: 1)只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定. 2)必须通过基类类型的引用或指针进行函数调用. 1.从派生类到基类的转换 因为每个派生类对象都包含基类部分,所以可以将基类类型的引用绑定到派生类对象的基类部分可以用指向基类的指针指向派生类对象: void print_total(const Item_

为什么基类的析构函数要写成虚函数?

为什么基类的析构函数要写成虚函数? 答:在实现多态时,当用基类操作派生类,在析构时防止只析构基类而不析构派生类的状况发生. 代码说明如下 第一段代码: 1 #include<iostream> 2 using namespace std;   3    4 class ClxBase   5 {public:   6 ClxBase() {}   7 ~ClxBase() {cout << "Output from the destructor of class ClxB

读书笔记 effective c++ Item 43 了解如何访问模板化基类中的名字

1. 问题的引入——派生类不会发现模板基类中的名字 假设我们需要写一个应用,使用它可以为不同的公司发送消息.消息可以以加密或者明文(未加密)的方式被发送.如果在编译阶段我们有足够的信息来确定哪个信息会被发送到哪个公司,我们可以使用基于模板的解决方案: 1 class CompanyA { 2 public: 3 ... 4 void sendCleartext(const std::string& msg); 5 void sendEncrypted(const std::string&