深入介绍信号和槽
信号和槽机制是Qt编程的基础。它可以让应用程序编程人员把这些互不了解的对象绑定在一起。前面,已经把一些信号和槽连接在了一起,也声明了自己的信号和槽,还实现了自己的槽,并且还发射了自己的信号。让我们再花一点时间,来进一步地了解这个机制。
槽和普通的C++成员函数几乎是一样的——可以是虚函数;可以被重载;可以是公有的、保护的或者私有的,并且也可以被其他C++成员函数直接调用;还有,它们的参数可以是任意类型。唯一的不同是:槽还可以和信号连接在一起,在这种情况下,每当发射这个信号的时候,就会自动调用这个槽。
connect()语句看起来会是如下的样子:
connect(sender, SIGNAL(signal), receiver, SLOT(slot));
这里的sender和receiver是指向QObject的指针,signal和slot是不带参数的函数名。实际上,SIGNAL()宏和SLOT()宏会把它们的参数转换成相应的字符串。
到目前位置,在已经看到的实例中,我们已经把不同的信号和不同的槽连接在了一起。但这里还需要考虑一些其他的可能性。
@一个信号可以连接多个槽
connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int)));
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(updateStatusBarIndicator(int)));
在发射这个信号的时候,会以不确定的顺序一个接一个地调用这些槽。
@多个信号可以连接同一个槽
connect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));
connect(calculator, SIGNAL(divisionByZero()), this, SLOT(handleMathError()));
无论发射的是哪一个信号,都会调用这个槽。
@一个信号可以与另外一个信号相连接
connect(lineEdit, SIGNAL(textChanged(const QString&)), this, SIGNAL(updateRecord(const QString&)));
当发射第一个信号时,也会发射第二个信号。除此之外,信号与信号之间的连接和信号与槽之间的连接是难以区分的。
@连接可以被移除
disconnect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));
这种情况较少用到,因为当删除对象时,Qt会自动移除和这个对象相关的所有连接。
要把信号成功连接到槽(或者连接到另外一个信号),它们的参数必须具有相同的顺序和相同的类型:
connect(ftp, SIGNAL(rawCommandRelpy(int, const QString&)), this, SLOT(processReply(int, const QString&)));
这里有个例外,如果信号的参数比它所连接的槽的参数多,那么多余的参数将会被简单地忽略掉:
connect(ftp, SIGNAL(rawCommandReply(int ,const QString&)), this, SLOT(checkErrorCode(int)));
如果参数类型不匹配,或者如果信号或槽不存在,则当应用程序使用调试模式构建后,Qt会在运行时发出警告。与之相类似的是,如果在信号和槽的名字中包含了参数名,Qt也会发出警告。
到现在为止,我们仅仅在窗口部件之间使用了信号和槽。但是这种机制本身是在QObject中实现的,并不只局限于图形用户界面编程中。这种机制可以用于任何QObject的子类中:
class Employee :public QObject{
Q_OBJECT
public:
Employee(){
mySalary = 0;
}
int salary()const{
return mySalary;
}
public slots:
void setSalary(int newSalary);
signals:
void salaryChanged(int newSalary);
private:
int mySalary;
};
void Employee::setSalary(int newSalary){
if (newSalary!=mySalary)
{
mySalary = newSalary;
emit salaryChanged(mySalary);
}
}
注意一下,setSalary()槽是如何工作的。只有在newSalary != mySalary的时候,才发射salaryChanged()信号。这样可以确保循环连接不会导致无限循环。
Qt的父-子对象机制:
Qt的父-子对象机制是在QObject中实现的。当利用一个父对象创建一个子对象(一个窗口部件、一个检验器、或是任意的其他类型)时,父对象会把这个子对象添加到自己的子对象列表中。当删除这个父对象时,它会遍历子对象列表并且删除每一个子对象。然后,这些子对象再去删除它们自己所包含的每个子对象。如此反复递归调用,直至清空所有子对象为止。这种父-子对象机制可在很大程度上简化内存管理工作,降低内存泄漏的风险。需要明确删除的对象是哪些使用new创建的并且没有父对象的对象。并且,如果在删除一个父对象之前先删除了它的子对象,Qt会自动地从它的父对象的子对象列表中将其移除。
对于窗口部件,父对象还有另外一层含义:子窗口部件会显示在它的父对象所在的区域中。当删除这个父窗口部件时,不仅子对象会从内存中消失,而且它也会在屏幕上小时。