背景知识:
基类 superClass
class superClass
{
public:
superClass()
{
std::string m = "superClass() " + std::to_string((long)this) + "\n";
std::cout << m << std::endl;
}
virtual ~superClass()
{
std::string m = "~superClass " + std::to_string((long)this) + "\n";
std::cout << m << std::endl;
}
virtual void print() const
{
std::string m = "printsuperClass " + std::to_string((long)this) + "\n" ;
std::cout << m << std::endl;
}
virtual void setA(int v_) const { }
private:
int a = 0;
};
子类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 &) |
不会被复制 |
|
|
by value same thread
superClass() 140737488346832
printsuperClass 140737488346416
~superClass 140737488346416
printsuperClass 140737488346416
~superClass 140737488346416
printsuperClass 140737488346416
~superClass 140737488346416
~superClass 140737488346848
~superClass 140737488346832
当生产者和消费者均在不同线程时:
mysignal(cn) |
会被复制 |
复制次数 =消费者个数*2 |
|
mysignal(cn &) |
不可用于多线程 |
|
|
mysignal(const cn &) |
会被复制 |
复制次数=消费者个数 |
|
by value different
superClass() 140737488346832
printsuperClass 140737018587504
~superClass 140737018587504
printsuperClass 140737010194800
~superClass 140737010194800
printsuperClass 140737001802096
~superClass 140737001802096
~superClass 9651648
~superClass 9498640
~superClass 9666912
~superClass 140737488346848
~superClass 140737488346832
解决方案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方式将直接的消费方式注册到统一的地方,由一个地方统一调用。这样的话,各个线程的方法会排队执行,失去多线程的意义。
综上,放弃父类子类设计方式。由统一一个类来实现所有功能。但是传递对象有效率问题,因为有多少个消费者,就会复制多少份对象。
爱恨难取舍。