(1)类型安全。需要关联的信号和槽的签名必须是等同的,即信号的参数类型和参数个数同接收该信号的槽的参数类型和参数个数相同。不过,一个槽的参数个数是可以少于信号的参数个数的,但缺少的参数必须是信号参数的最后一个或几个参数。如果信号和槽的签名不符,编译器就会报错。
(2)松散耦合。信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无需知道是哪个对象的哪个槽需要接收它发出的信号,它只需在适当的时间发送适当的信号就可以了,而不需要知道也不关心它的信号有没有被接收到,更不需要知道是哪个对象的哪个槽收到了信号。同样的,对象的槽也不知道是哪些信号关联了自己,而一旦关联信号和槽,Qt就保证了适合的槽得到了调用。即使关联的对象在运行时被删除,应用程序也不会崩溃。
(3)信号和槽机制增强了对象间通信的灵活性。一个信号可以关联多个槽,也可以多个信号关联一个槽。
同回调函数相比,信号和槽机制运行速度有些慢。通过传递一个信号来调用槽函数将会比直接调用非虚函数运行速度慢10倍。原因如下:
(1)需要定位接收信号的对象;
(2)安全地遍历所有的关联(如一个信号关联多个槽的情况);
(3)编组/解组传递的参数;
(4)多线程的时候,信号可能需要排队等待。
然而,与创建对象的new操作及删除对象的delete操作相比,信号和槽的运行代价只是他们很少的一部分。信号和槽机制导致的这点性能损耗,对实时应用程序是可以忽略的。
创建一个类, 让其从QPushButton类派生, 重写该类中的事件处理器函数
方法一:
1>. enterEvent() – 光标进入
2>. leaveEvent() – 光标离开
3>. mousePressEvent() – 鼠标按下
4>. paintEvent() – 刷新背景图
方法二:
通过setstylesheet设置
回调函数
QTextStream – 文本流, 操作轻量级数据(int, double, QString), 数据写入文件中之后以文本的方式呈现。
QDataStream – 数据流, 通过数据流可以操作各种数据类型, 包括类对象, 存储到文件中数据可以还原到内存(二进制)。
QTextStream, QDataStream可以操作磁盘文件, 也可以操作内存数据, 通过流对象可以将数据打包到内存, 进行数据的传输.
服务器端:
QT下udp通信服务器端和客户端的关系是对等的, 做的处理也是一样的.
方法一:
方法二:
方法三:
QFuture fut1 = QtConcurrent::run(processFun, command);
processFun为线程回调函数
多线程使用注意事项:
可以通过connect的第五个参数进行控制信号槽执行时所在的线程
connect有几种连接方式,直接连接和队列连接、自动连接
直接连接:信号槽在信号发出者所在的线程中执行
队列连接:信号在信号发出者所在的线程中执行,槽函数在信号接收者所在的线程中执行
自动连接:多线程时为队列连接函数,单线程时为直接连接函数。
Qt的信号和槽原理就是回调函数。所以,我们需要保存对象绑定的回调函数
template
class SlotBase
{
public:virtual void Exec(T param1) = 0; //纯虚函数virtual ~SlotBase(){}
};/*
* func: 槽函数
* parm:
* return:
*/
template
class Slot : public SlotBase
{
public:/* 定义Slot的时候,获取槽函数信息 */Slot(T* pObj, void (T::*func)(T1)){m_pSlotBase = pObj;m_Func = func;}/* signal触发时,调用 */void Exec(T1 param1){(m_pSlotBase->*m_Func)(param1);}private:/* 槽函数信息 暂存 */T* m_pSlotBase;void (T::*m_Func)(T1);
};
重要阐述:
1.创建一个Signal 类,该类保主要是保存多个Slot对象,当一个信号发送时,会遍历这个表,对每一个slot绑定的回调函数进行调用。
2.重载运算符(), 遍历这个表,调用回调函数,即signal触发机制
3.写一个绑定函数Bind,用于将Slot对象添加到槽表中
template
class Signal
{
public:/* 模板函数 -> Bind时获取槽函数指针 */templatevoid Bind(T* pObj, void (T::*func)(T1)){m_pSlotSet.push_back(new Slot(pObj,func));}/* 重载操作符 -> signal触发机制 */void operator()(T1 param1){for(int i=0;i<(int)m_pSlotSet.size();i++){m_pSlotSet[i]->Exec(param1);}}~Signal(){for(int i=0;i<(int)m_pSlotSet.size();i++){delete m_pSlotSet[i];}}private:vector*> m_pSlotSet; //这一句很重要,靠基类的指针来存储 信号槽指针
};
测试类包含多个signal 当调用接口就将调用signal的()函数,从而调用slot
class TestSignal
{
public:TestSignal(){}void setValue(int value){emit ValueChanged(value);}void setfValue(int value){emit ValueChanged_f(value);}public slots:void FuncOfA(int parm){printf("enter FuncOfA parm = %d\n", parm);}void FuncOfB(int parm){printf("enter FuncOfB parm = %d\n", parm);}signals:Signal ValueChanged;Signal ValueChanged_f;
};
#define Connect(sender, signal, receiver, method) ((sender)->signal.Bind(receiver, method))
1、用户自定义需要先注册一个类型,即使用qRegisterMetaType,注册到QT的一个Vector中
2、QVariant里面会new一个用户自定义类型的内存,并调用拷贝构造函数,QVariant自身的赋值会使用共享内存管理
所以用户可以传入一个临时变量地址,如果用户传入的是一个指针,这个指针需要用户自己析构,改变这个指针的值,并不会改变QVariant,因为是两个不同的空间了
而如果QVariant a1=b1(b1是QVariant),改变b1的值会改变a1的。因为这样用的是shared指针
初看2以为是对的,验证发现不准确,改变b1并没有改变a1的值,细看发现这里面有QT使用了个小技巧,要取b1的值然后改变时,会调用data函数
CVariantHelp* pBTemp = reinterpret_cast
pBTemp->j_ = 99;
而data的实现会调用detach将shared分离
void* QVariant::data()
{
detach();
return const_cast
}
void QVariant::detach()
{
if (!d.is_shared || d.data.shared->ref == 1)
return;
Private dd;
dd.type = d.type;
handler->construct(&dd, constData());
if (!d.data.shared->ref.deref())handler->clear(&d);
d.data.shared = dd.data.shared;
}
无论是QScopedPointer 还是 std::unique_ptr 都拥有一个很好的名字,它向代码的阅读者传递了明确的信息:这个智能指针只能在本作用域里使用,不希望被转让。因为它的拷贝构造和赋值操作都是私有的,这点我们可以对比QObject及其派生类的对象哈。
QSharedPointer 与 QScopedPointer 一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针 ,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。shared_ptr也可以安全地放到标准容器中,并弥补了std::auto_ptr 和 QScopedPointer 因为转移语义而不能把指针作为容器元素的缺陷。
QWeakPointer 是为配合 QSharedPointer 而引入的一种智能指针,它更像是 QSharedPointer 的一个助手(因为它不具有普通指针的行为,没有重载operator*和->)。它的最大作用在于协助 QSharedPointer 工作,像一个旁观者一样来观测资源的使用情况。
weak_ptr 主要是为了避免强引用形成环状。摘自msdn中一段话:
A cycle occurs when two or more resources controlled by shared_ptr objects hold mutually referencing shared_ptr objects. For example, a circular linked list with three elements has a head node N0; that node holds a shared_ptr object that owns the next node, N1; that node holds a shared_ptr object that owns the next node, N2; that node, in turn, holds a shared_ptr object that owns the head node, N0, closing the cycle. In this situation, none of the reference counts will ever become zero, and the nodes in the cycle will not be freed. To eliminate the cycle, the last node N2 should hold a weak_ptr object pointing to N0 instead of a shared_ptr object. Since the weak_ptr object does not own N0 it doesn’t affect N0’s reference count, and when the program’s last reference to the head node is destroyed the nodes in the list will also be destroyed.
在Qt中,对于QObject及其派生类对象,QWeakPointer有特殊处理。它可以作为QPointer的替代品
这种情况下,不需要QSharedPointer的存在
5. QSharedDataPointer
这是为配合 QSharedData 实现隐式共享(写时复制 copy-on-write))而提供的便利工具。
Qt中众多的类都使用了隐式共享技术,比如QPixmap、QByteArray、QString、…。而我们为自己的类实现隐式共享也很简单,比如要实现一个 Employee类:
定义一个只含有一个数据成员(QSharedDataPointer) 的 Employee 类
我们需要的所有数据成员放置于 派生自QSharedData的 EmployeeData类中。
QExplicitlySharedDataPointer 和 QSharedDataPointer 非常类似,但是它禁用了写时复制功能。这使得我们创建的对象更像一个指针。
保持一个库中的所有公有类的大小恒定的问题可以通过单独的私有指针给予解决。这个指针指向一个包含所有数据的私有数据结构体。这个结构体的大小可以随意改变而不会产生副作用,应用程序只使用相关的公有类,所使用的对象大小永远不会改变,它就是该指针的大小。这个指针就被称作D指针。
D指针的其他好处
1.隐藏实现细节——我们可以不提供widget.cpp文件而只提供WidgetLib和相应的头文件和二进制文件。
2.头文件中没有任何实现细节,可以作为API使用。
3.由于原本在头文件的实现部分转移到了源文件,所以编译速度有所提高。
4.二进制兼容
其实以上的点都很细微,自己跟过源代码的人都会了解,qt是隐藏了d指针的管理和核心源的实现。像是在_p.h中部分函数的声明,qt也宣布在以后版本中将会删除。( This file is not part of the Qt API. It exists purely as an implementation detail. This header file may change from version to version without notice, or even be removed.)
q_ptr指针指向父类,使用如下宏定义辅助函数和声明友元类
#ifndef D_PTR_H
#define D_PTR_H#include template static inline T *GetPtrHelper(T *ptr) { return ptr; }#define DECLARE_PRIVATE(Class) \inline Class##Private* d_func() { return reinterpret_cast(GetPtrHelper(d_ptr)); } \inline const Class##Private* d_func() const { return reinterpret_cast(GetPtrHelper(d_ptr)); }\friend class Class##Private;#define DPTR(Class) Class##Private * const d = d_func()class MyClassPrivate;class MyClass : public QObject {Q_OBJECT
public:explicit MyClass(QObject *parent = 0);virtual ~MyClass();void testFunc();protected:MyClass(MyClassPrivate &d);private:MyClassPrivate * const d_ptr;DECLARE_PRIVATE(MyClass);MyClass(const MyClass&);MyClass& operator= (const MyClass&);
};#endif #ifndef Q_PTR_H
#define Q_PTR_H#include
#include "d_ptr.h"#define DECLARE_PUBLIC(Class) \inline Class* q_func() { return static_cast(q_ptr); } \inline const Class* q_func() const { return static_cast(q_ptr); } \friend class Class;#define QPTR(Class) Class * const q = q_func()class MyClassPrivate : public QObject
{
Q_OBJECTpublic:MyClassPrivate(MyClass *q, QObject *parent = 0);virtual ~MyClassPrivate() {}signals:void testSgnl();private slots:void testSlt();public:void fool();private:MyClass * const q_ptr;DECLARE_PUBLIC(MyClass);
};#endif
上一篇:Class 加载的过程