欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

c++ Qt信號(hào)槽原理

 更新時(shí)間:2021年02月05日 08:51:32   作者:sherlock_lin  
這篇文章主要介紹了c++ Qt信號(hào)槽原理的相關(guān)資料,幫助大家更好的理解和使用c++,感興趣的朋友可以了解下

1、說(shuō)明

使用Qt已經(jīng)好幾年了,一直以為自己懂Qt,熟悉Qt,使用起來(lái)很是熟練,無(wú)論什么項(xiàng)目,都喜歡用Qt編寫。但真正去看Qt的源碼,去理解Qt的思想也就近兩年的事。

本次就著重介紹一下Qt的核心功能--信號(hào)槽機(jī)制,相信接觸過Qt的人都能很熟悉地使用,甚至,大部分人還能輕松地說(shuō)出信息槽的幾種用法。但是信號(hào)槽的核心可不是簡(jiǎn)單說(shuō)說(shuō)就能說(shuō)清楚的。

那么,本次,就從Qt的源碼中講解一下信號(hào)槽的機(jī)制。

其實(shí),直到寫這篇文章,我也沒有完全看明白相關(guān)的源碼,只是明白了其中的大部分以及使用機(jī)制,其中還有很多細(xì)節(jié)的,留待以后整理。

如果錯(cuò)誤還請(qǐng)大家指正。

2、環(huán)境以及知識(shí)點(diǎn)

Qt版本:Qt 5.5.1

系統(tǒng):windows 10

在閱讀本文前,希望你能:

  1. 熟練使用C++,了解make的編譯方法和過程;
  2. 熟練使用Qt的信號(hào)槽功能,對(duì)信號(hào)槽的寫法以及4和5的區(qū)別了如指掌;
  3. QMetaObject元數(shù)據(jù)系統(tǒng);
  4. 懂一些設(shè)計(jì)模式,能理解觀察者模式;

3、信號(hào)槽源碼分析

以下將按照SIGNAL/SLOT宏定義連接信號(hào)槽的方式做講解

接下來(lái)將會(huì)從按照以下的步驟來(lái)進(jìn)行分析:

  1. Qt元數(shù)據(jù)系統(tǒng);
  2. moc預(yù)編譯;
  3. Q_OBJECT宏;
  4. signals和slots關(guān)鍵字以及emit;
  5. SIGNAL()和SLOT()宏;
  6. connect 方法;
  7. 觸發(fā)信號(hào);

3.1、Qt的元數(shù)據(jù)系統(tǒng)

沒看過Qt源碼的同學(xué)可能會(huì)對(duì)QMetaObject有些陌生,我們打開Qt手冊(cè),查看此類的說(shuō)明,介紹如下:

The QMetaObject class contains meta-information about Qt objects.

The Qt Meta-Object System in Qt is responsible for the signals and slots inter-object communication mechanism, runtime type information, and the Qt property system. A single QMetaObject instance is created for each QObject subclass that is used in an application, and this instance stores all the meta-information for the QObject subclass. This object is available as QObject::metaObject().

這里是說(shuō),QMetaObject包含了Qt的元對(duì)象信息。元對(duì)象機(jī)制類似Java的反射機(jī)制。通過繼承QObject,并在定義類是添加一定Qt內(nèi)置宏,能在運(yùn)行時(shí)動(dòng)態(tài)獲取Qt的信號(hào)槽、類型信息以及相關(guān)屬性。

一個(gè)簡(jiǎn)答的例子

void MainWindow::onClickButton()
{
  qDebug()<<"on click button";
  const QMetaObject* metaObject = this->metaObject();
  qDebug()<<metaObject->className();
  qDebug()<<metaObject->superClass()->className();

  int methodIndex = metaObject->indexOfMethod("testFunction()");
  qDebug()<<methodIndex;
  qDebug()<<metaObject->method(methodIndex).name();
  metaObject->method(methodIndex).invoke(this);

  QMetaObject::invokeMethod(this, "testFunction");
}

如上,一個(gè)簡(jiǎn)單的例子,通過QMetaObject,我們得到了該對(duì)象的類名、父類名、方法并調(diào)用了該方法

怎么樣,熟悉Java的小伙伴已經(jīng)發(fā)現(xiàn)了,這不就是Java的反射嗎,誰(shuí)說(shuō)C++沒有反射呢

那么,Qt是如何實(shí)現(xiàn)”反射“的呢?答案是使用moc預(yù)編譯

3.2、moc編譯

moc全稱Meta-Object Compiler,即元對(duì)象編譯器。我們可以在Qt的安裝目錄的bin文件下看到moc工具,moc.exe。Qt的構(gòu)建的時(shí)候,會(huì)調(diào)用該工具生成moc文件,我們?cè)诰幾g目錄下看到的moc_xxx.cpp文件就是該工具生成的。

Qt的MinGW版本使用的是qmake進(jìn)行項(xiàng)目管理,它和cmake功能類似,但沒有后者強(qiáng)大。使用qmake生成Makefile后,我們打開Makefile文件,我們可以狠清楚地看到有一個(gè)調(diào)用moc.exe工具的地方,代碼太多,就不列出來(lái)了。

此外,我們還發(fā)現(xiàn),并不是所有的代碼都會(huì)生成moc_xxx.cpp文件的,只有使用了 Q_OBJECT 宏的類文件,才會(huì)生成。沒有錯(cuò),moc工具就是根據(jù) Q_OBJECT 宏來(lái)生成moc_xxx.cpp文件的,而實(shí)現(xiàn)“反射”的元數(shù)據(jù)系統(tǒng)的也是依靠Q_OBJECT的。

到此,我們其實(shí)已經(jīng)能夠大概理清qmake項(xiàng)目的構(gòu)建步驟了。步驟和常用的cmake項(xiàng)目類似,區(qū)別就是,qmake生成的Makefile文件種,會(huì)寫有調(diào)用moc工具的指令,以達(dá)到moc_xxx.cpp文件的生成。

我們可以使用moc工具手動(dòng)生成moc_xxx.cpp,使用指令 moc.exe mainwindow.h,即會(huì)在控制臺(tái)打印moc文件信息,也可以使用 -o 參數(shù)來(lái)將生成的內(nèi)容寫入文件,其余參數(shù)可以使用 moc.exe -h 來(lái)查看

3.3、Q_OBJECT

我們可以從源代碼中查看 Q_OBJECT 的內(nèi)容,這里調(diào)整一個(gè)格式,使用 Q_OBJECT 宏之后,會(huì)在類定義的開頭多出以下代碼:

public:
  Q_OBJECT_CHECK
  QT_WARNING_PUSH
  Q_OBJECT_NO_OVERRIDE_WARNING
  static const QMetaObject staticMetaObject;
  virtual const QMetaObject *metaObject() const;
  virtual void *qt_metacast(const char *);
  virtual int qt_metacall(QMetaObject::Call, int, void **);
  QT_WARNING_POP
  QT_TR_FUNCTIONS
private:
  Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
  struct QPrivateSignal {};

可以看到,這里多出幾個(gè)方法和一些變量

  1. 屬性staticMetaObject,元數(shù)據(jù)對(duì)象,可以從中獲取當(dāng)前類的元數(shù)據(jù);
  2. 方法metaObject(),獲取元數(shù)據(jù)對(duì)象指針,大多數(shù)情況下,返回staticMetaObject指針;
  3. 方法qt_metacast(),原數(shù)據(jù)對(duì)象類型轉(zhuǎn)換,轉(zhuǎn)換成指定的類型,使用時(shí)一般傳入父類的名稱字符串;
  4. 方法qt_metacall(),執(zhí)行函數(shù)的回調(diào),信號(hào)觸發(fā);
  5. 方法qt_static_metacall(),回調(diào)函數(shù),被qt_metacall()調(diào)用,內(nèi)部執(zhí)行槽;

這里的幾個(gè)方法都沒有實(shí)現(xiàn)體,因?yàn)閷?shí)現(xiàn)部分會(huì)有 moc 工具生成,在moc_xxx.cpp 文件中可以查看這些方法的實(shí)現(xiàn)體

3.4、signals和slots

signals 用于聲明自定義信號(hào),slots 用于聲明槽函數(shù),emit 用于發(fā)送信號(hào),我們可以從源碼中查看這三個(gè)宏定義

define slots
define signals public
define emit

可以看出,這三個(gè)宏幾乎什么都沒有做,signals 就是聲明所謂的信號(hào)是public方法,而slots和emit更是為空,標(biāo)準(zhǔn)C++在編譯的時(shí)候,根本不受這三個(gè)宏的影響,那么它們的用處在哪里呢?在moc工具調(diào)用和connect連接的時(shí)候。

打開moc_xxx.cpp文件,對(duì)比查看信號(hào)

signals:
  void clickButton(int value);

  void clickButton2();
// SIGNAL 0
void MainWindow::clickButton(int _t1)
{
  void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
  QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

// SIGNAL 1
void MainWindow::clickButton2()
{
  QMetaObject::activate(this, &staticMetaObject, 1, Q_NULLPTR);
}

其實(shí),信號(hào)就是方法,而 emit clickButton() 發(fā)送信號(hào),就是調(diào)用 clickButton() 方法,換言之,觸發(fā)信號(hào),就算不要emit也無(wú)妨

3.5、SIGNAL()和SLOT()

查看源碼

# define SLOT(a)   qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a)  qFlagLocation("2"#a QLOCATION)

qFlagLocation() 源碼如下:

const char *qFlagLocation(const char *method)
{
  QThreadData *currentThreadData = QThreadData::current(false);
  if (currentThreadData != 0)
    currentThreadData->flaggedSignatures.store(method);
  return method;
}

store() 方法

void store(const char* method)
{ locations[idx++ % Count] = method; }

所以 SIGNAL(clickButton()) 宏展開為 qFlagLocation("2"clickButton(int) QLOCATION)

SLOT() 同理,這里的1和2,最后會(huì)添加到信號(hào)槽的前面,其實(shí)是為了區(qū)分信號(hào)和槽,源碼中還有一個(gè)0在 METHOD() 宏

qFlagLocation 方法的作用是將信號(hào)槽轉(zhuǎn)換成字符串保存起來(lái),store 方法中,locations是個(gè)二維數(shù)組,而 idx 每次都加一,保證信號(hào)和槽的不同的方法存儲(chǔ)在不同的數(shù)組中。

我們也可以在代碼中打印出來(lái)看下:

qDebug()<<SIGNAL(clickButton(int));	//2clickButton(int)
qDebug()<<SLOT(onClickButton());	//1onClickButton()

3.6、connect方法

最后,就是最關(guān)鍵的connect方法,做了一些簡(jiǎn)單的注釋

QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
                   const QObject *receiver, const char *method,
                   Qt::ConnectionType type)
{
  if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
    qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
         sender ? sender->metaObject()->className() : "(null)",
         (signal && *signal) ? signal+1 : "(null)",
         receiver ? receiver->metaObject()->className() : "(null)",
         (method && *method) ? method+1 : "(null)");
    return QMetaObject::Connection(0);
  }
  QByteArray tmp_signal_name;

  if (!check_signal_macro(sender, signal, "connect", "bind"))
    return QMetaObject::Connection(0);
  const QMetaObject *smeta = sender->metaObject();//獲取發(fā)送者的元數(shù)據(jù)對(duì)象
  const char *signal_arg = signal;//信號(hào)
  ++signal; //skip code
  QArgumentTypeArray signalTypes;//信號(hào)參數(shù)類型數(shù)組
  Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);
  //信號(hào)轉(zhuǎn)換為簽名,并得到信號(hào)參數(shù)類型數(shù)組
  QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
  //找到信號(hào)索引
  int signal_index = QMetaObjectPrivate::indexOfSignalRelative(
      &smeta, signalName, signalTypes.size(), signalTypes.constData());
  //小于0表示,表示信號(hào)索引有問題
  if (signal_index < 0) {
    // check for normalized signatures
    //將信號(hào)重新規(guī)范化,再進(jìn)行上面的簽名轉(zhuǎn)換,并重新得到索引
    tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
    signal = tmp_signal_name.constData() + 1;

    //重新進(jìn)行簽名轉(zhuǎn)換,并得到參數(shù)類型列表
    signalTypes.clear();
    signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
    smeta = sender->metaObject();
    signal_index = QMetaObjectPrivate::indexOfSignalRelative(
        &smeta, signalName, signalTypes.size(), signalTypes.constData());
  }
  //重新獲取的信號(hào)索引還是無(wú)效,則是頭文件中信號(hào)的定義出錯(cuò),找不到信號(hào),報(bào)錯(cuò)信號(hào)不存在
  if (signal_index < 0) {
    err_method_notfound(sender, signal_arg, "connect");
    err_info_about_objects("connect", sender, receiver);
    return QMetaObject::Connection(0);
  }
  //根據(jù)當(dāng)前信號(hào)的索引找到最原始的信號(hào)的索引,因?yàn)樾盘?hào)是可以被繼承,這里找的祖先信號(hào)
  signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
  signal_index += QMetaObjectPrivate::signalOffset(smeta);//信號(hào)的索引再加上信號(hào)的偏移量

  QByteArray tmp_method_name;
  //提取槽的編碼,應(yīng)該是QSLOT_CODE或者QSIGNAL_CODE,用于判斷槽是信號(hào)還是方法
  int membcode = extract_code(method);

  //檢查槽編碼,槽可以是槽函數(shù)或者信號(hào),初次以為,都無(wú)效
  if (!check_method_code(membcode, receiver, method, "connect"))
    return QMetaObject::Connection(0);
  const char *method_arg = method;
  ++method; // skip code

  QArgumentTypeArray methodTypes;
  //轉(zhuǎn)換槽簽名,并獲取槽的參數(shù)類型列表
  QByteArray methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
  const QMetaObject *rmeta = receiver->metaObject();//獲取接受者的元數(shù)據(jù)對(duì)象
  int method_index_relative = -1;
  Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7);
  switch (membcode) {
  case QSLOT_CODE://接受者是槽函數(shù)
    method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
        &rmeta, methodName, methodTypes.size(), methodTypes.constData());
    break;
  case QSIGNAL_CODE://接受者是信號(hào)
    method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
        &rmeta, methodName, methodTypes.size(), methodTypes.constData());
    break;
  }
  //槽的索引為-1,表示無(wú)效
  if (method_index_relative < 0) {
    // check for normalized methods
    //將槽進(jìn)行規(guī)范化處理,并重新轉(zhuǎn)換槽簽名
    tmp_method_name = QMetaObject::normalizedSignature(method);
    method = tmp_method_name.constData();

    methodTypes.clear();
    methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
    // rmeta may have been modified above
    //接受者元數(shù)據(jù)對(duì)象前面可能被修改過,這里重新獲取
    rmeta = receiver->metaObject();
    //重新獲取槽的索引
    switch (membcode) {
    case QSLOT_CODE:
      method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(
          &rmeta, methodName, methodTypes.size(), methodTypes.constData());
      break;
    case QSIGNAL_CODE:
      method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(
          &rmeta, methodName, methodTypes.size(), methodTypes.constData());
      break;
    }
  }

  //如果還找不到,則說(shuō)明槽定義有誤,報(bào)錯(cuò)
  if (method_index_relative < 0) {
    err_method_notfound(receiver, method_arg, "connect");
    err_info_about_objects("connect", sender, receiver);
    return QMetaObject::Connection(0);
  }

  //檢查信號(hào)和槽的參數(shù)
  if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(),
                       methodTypes.size(), methodTypes.constData())) {
    qWarning("QObject::connect: Incompatible sender/receiver arguments"
         "\n    %s::%s --> %s::%s",
         sender->metaObject()->className(), signal,
         receiver->metaObject()->className(), method);
    return QMetaObject::Connection(0);
  }

  int *types = 0;
  //隊(duì)列連接檢查,參數(shù)要是基本類型,或者使用元數(shù)據(jù)注冊(cè)
  if ((type == Qt::QueuedConnection)
      && !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {
    return QMetaObject::Connection(0);
  }

#ifndef QT_NO_DEBUG
  //打印調(diào)試信息
  QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
  QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
  check_and_warn_compat(smeta, smethod, rmeta, rmethod);
#endif
  QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
    sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));
  return handle;
}
方法代碼很多很雜,但無(wú)非就是檢查信號(hào)槽的格式,獲取參數(shù)列表, 最后保存起來(lái)

QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
c->sender = s; //發(fā)送者對(duì)象
c->signal_index = signal_index; //信號(hào)索引
c->receiver = r;  //接受者對(duì)象
c->method_relative = method_index; //槽索引
c->method_offset = method_offset;  //槽偏移
c->connectionType = type;      //連接方式
c->isSlotObject = false;
c->argumentTypes.store(types);
c->nextConnectionList = 0;
c->callFunction = callFunction;//靜態(tài)回調(diào)函數(shù)

//在發(fā)送者元數(shù)據(jù)內(nèi)加上連接信息
//信號(hào)發(fā)送者的對(duì)象內(nèi)存中保存了連接的信息,包括槽的對(duì)象,槽地址,連接方式等
QObjectPrivate::get(s)->addConnection(signal_index, c.data());

3.7、觸發(fā)信號(hào)

這時(shí)候再回過頭來(lái)看3.4中的信號(hào)觸發(fā),我們知道,emit信號(hào)就是調(diào)用moc文件中的方法,方法的核心就是 QMetaObject::activate()

直接看該方法中調(diào)用槽函數(shù)的一段

//因?yàn)橐粋€(gè)信號(hào)可能連接多個(gè)槽,這里循環(huán)遍歷鏈表進(jìn)行調(diào)用
do {
	QObjectPrivate::Connection *c = list->first;
	if (!c) continue;
	// We need to check against last here to ensure that signals added
	// during the signal emission are not emitted in this emission.
	QObjectPrivate::Connection *last = list->last;

	do {
		if (!c->receiver)
			continue;

		QObject * const receiver = c->receiver;
		const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId;

		// determine if this connection should be sent immediately or
		// put into the event queue
		//直接連接并且發(fā)送和接受不再一個(gè)線程中,或者隊(duì)列連接,則放入事件隊(duì)列中
		//可知,直接連接并且發(fā)送和接受不在同一個(gè)線程,則效果和隊(duì)列連接相同
		if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
			|| (c->connectionType == Qt::QueuedConnection)) {
			queued_activate(sender, signal_index, c, argv ? argv : empty_argv, locker);
			continue;
#ifndef QT_NO_THREAD
			//阻塞式隊(duì)列連接
		} else if (c->connectionType == Qt::BlockingQueuedConnection) {
			locker.unlock();
			//在同一個(gè)線程,則報(bào)錯(cuò)
			if (receiverInSameThread) {
				qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
				"Sender is %s(%p), receiver is %s(%p)",
				sender->metaObject()->className(), sender,
				receiver->metaObject()->className(), receiver);
			}
			QSemaphore semaphore;//資源計(jì)數(shù)器,avail為0
			QMetaCallEvent *ev = c->isSlotObject ?
				new QMetaCallEvent(c->slotObj, sender, signal_index, 0, 0, argv ? argv : empty_argv, &semaphore) :
				new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal_index, 0, 0, argv ? argv : empty_argv, &semaphore);
			//根據(jù)連接信息構(gòu)造一個(gè)事件,并添加到接受者的 事件隊(duì)列中
			QCoreApplication::postEvent(receiver, ev);
			//信號(hào)發(fā)送者的線程阻塞,acquire資源數(shù)為1,>avail(0),這里阻塞
			//當(dāng)槽執(zhí)行玩之后釋放,這里的avail才會(huì)增加,阻塞結(jié)束
			semaphore.acquire();
			locker.relock();
			continue;
#endif
		}

		QConnectionSenderSwitcher sw;

		if (receiverInSameThread) {
			sw.switchSender(receiver, sender, signal_index);
		}
		const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;
		const int method_relative = c->method_relative;
		if (c->isSlotObject) {
			c->slotObj->ref();
			QScopedPointer<QtPrivate::QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);
			locker.unlock();
			obj->call(receiver, argv ? argv : empty_argv);

			// Make sure the slot object gets destroyed before the mutex is locked again, as the
			// destructor of the slot object might also lock a mutex from the signalSlotLock() mutex pool,
			// and that would deadlock if the pool happens to return the same mutex.
			obj.reset();

			locker.relock();
		} else if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
			//we compare the vtable to make sure we are not in the destructor of the object.
			locker.unlock();
			const int methodIndex = c->method();
			if (qt_signal_spy_callback_set.slot_begin_callback != 0)
				qt_signal_spy_callback_set.slot_begin_callback(receiver, methodIndex, argv ? argv : empty_argv);

			callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);

			if (qt_signal_spy_callback_set.slot_end_callback != 0)
				qt_signal_spy_callback_set.slot_end_callback(receiver, methodIndex);
			locker.relock();
		} else {
			const int method = method_relative + c->method_offset;
			locker.unlock();

			if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
				qt_signal_spy_callback_set.slot_begin_callback(receiver,
															method,
															argv ? argv : empty_argv);
			}

			metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);

			if (qt_signal_spy_callback_set.slot_end_callback != 0)
				qt_signal_spy_callback_set.slot_end_callback(receiver, method);

			locker.relock();
		}

		if (connectionLists->orphaned)
			break;
	} while (c != last && (c = c->nextConnectionList) != 0);

	if (connectionLists->orphaned)
		break;
} while (list != &connectionLists->allsignals &&
	//start over for all signals;
	((list = &connectionLists->allsignals), true));

由上面代碼,我們大概可以理解信號(hào)槽的幾種連接方式:

  1. 默認(rèn)連接并且信號(hào)槽的對(duì)象不在同一個(gè)線程中,則效果和隊(duì)列連接類似;
  2. 阻塞時(shí)隊(duì)列連接,信號(hào)和槽對(duì)象不同處于同一個(gè)線程中;
  3. Qt使用QSemaphore來(lái)實(shí)現(xiàn)阻塞式的槽函數(shù)調(diào)用;

4、小結(jié)

本次的源碼因?yàn)榉N種原因,看的不是很詳細(xì),但是理解Qt的信號(hào)槽機(jī)制綽綽有余了

  1. Qt自帶的元數(shù)據(jù)系統(tǒng)利用C++的宏等特性實(shí)現(xiàn)反射機(jī)制;
  2. 利用元數(shù)據(jù)系統(tǒng),在連接信號(hào)槽是將槽的信息(接收對(duì)象、槽方法、參數(shù)列表、連接方式等)保存在信號(hào)的元數(shù)據(jù)中;
  3. 信號(hào)也是方法,方法體有moc工具生成,方法內(nèi)獲取該信號(hào)連接的所有槽信息,并依序執(zhí)行;

直到這里,信號(hào)槽的邏輯已經(jīng)顯而易見了,它就是一個(gè)變種的觀察者模式,槽的信息保存在信號(hào)對(duì)象中也就是設(shè)置回調(diào)函數(shù),觸發(fā)信號(hào)也就是執(zhí)行回調(diào)函數(shù),只是Qt庫(kù)將其中的各種操作細(xì)節(jié)封裝起來(lái)了,所以,使用起來(lái),不去關(guān)注設(shè)計(jì)模式的細(xì)節(jié),也就容易很多了。不得不說(shuō),無(wú)論是從設(shè)計(jì)思路,還是開發(fā)技巧上看,Qt的開發(fā)者真的很牛叉。

5、第三方信號(hào)槽庫(kù)

信號(hào)槽機(jī)制是Qt首創(chuàng),但不是其獨(dú)有,其他各類C++流行框架也都是互相借鑒,C++標(biāo)準(zhǔn)庫(kù)的預(yù)備役的boost中也有信號(hào)槽機(jī)制的實(shí)現(xiàn)。如果平時(shí)開發(fā)中需要用到信號(hào)槽機(jī)制,但是又不想引入這些龐大的類庫(kù),可以使用輕量級(jí)別的信號(hào)槽庫(kù):http://sigslot.sourceforge.net,該庫(kù)不詳細(xì)介紹,有興趣的小伙伴自己學(xué)習(xí)把。

以上就是c++ Qt信號(hào)槽原理的詳細(xì)內(nèi)容,更多關(guān)于c++ Qt信號(hào)槽的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++ 中將一維數(shù)組轉(zhuǎn)成多維的三種方式示例詳解

    C++ 中將一維數(shù)組轉(zhuǎn)成多維的三種方式示例詳解

    這篇文章主要介紹了C++ 中將一維數(shù)組轉(zhuǎn)成多維的三種方式,每種方式結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2023-12-12
  • C++歸并排序算法實(shí)例

    C++歸并排序算法實(shí)例

    這篇文章主要介紹了C++歸并排序算法實(shí)例,本文先是介紹了什么是歸并排序,然后給出了實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2014-10-10
  • 淺析如何在c語(yǔ)言中調(diào)用Linux腳本

    淺析如何在c語(yǔ)言中調(diào)用Linux腳本

    如何在c語(yǔ)言中調(diào)用Linux腳本呢?下面小編就為大家詳細(xì)的介紹一下吧!需要的朋友可以過來(lái)參考下
    2013-08-08
  • C++如何獲取當(dāng)前系統(tǒng)時(shí)間及格式化輸出

    C++如何獲取當(dāng)前系統(tǒng)時(shí)間及格式化輸出

    這篇文章主要介紹了C++如何獲取當(dāng)前系統(tǒng)時(shí)間及格式化輸出的實(shí)例代碼,主要用到time()及strftime()函數(shù),通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • OpenCV實(shí)現(xiàn)更改圖片顏色功能

    OpenCV實(shí)現(xiàn)更改圖片顏色功能

    這篇文章主要為大家詳細(xì)介紹了如何利用OpenCV實(shí)現(xiàn)更改圖片顏色的功能,文中代碼介紹詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • C++類的特種函數(shù)生成機(jī)制詳解

    C++類的特種函數(shù)生成機(jī)制詳解

    這篇文章主要給大家介紹了關(guān)于C++類特種函數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09
  • VS2013安裝配置和使用Boost庫(kù)教程

    VS2013安裝配置和使用Boost庫(kù)教程

    這篇文章主要為大家詳細(xì)介紹了VS2013安裝配置和使用Boost庫(kù)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • NSString與C字符串之間的相互轉(zhuǎn)換

    NSString與C字符串之間的相互轉(zhuǎn)換

    本文將詳細(xì)介紹NSString與C字符串之間的相互轉(zhuǎn)換,需要的朋友可以參考下
    2012-11-11
  • C語(yǔ)言掃雷排雷小游戲?qū)崿F(xiàn)全程

    C語(yǔ)言掃雷排雷小游戲?qū)崿F(xiàn)全程

    本篇我將帶領(lǐng)大家攻克掃雷游戲各處難點(diǎn),讓你寫掃雷不在困難,我們的掃雷游戲可以實(shí)現(xiàn)標(biāo)記雷的功能和展開一片的功能。我們將分三個(gè)文件為大家介紹,分別為test.c,game.h和game.c
    2022-05-05
  • c語(yǔ)言使用fdk_aac實(shí)現(xiàn)aac音頻解碼為pcm

    c語(yǔ)言使用fdk_aac實(shí)現(xiàn)aac音頻解碼為pcm

    這篇文章主要為大家詳細(xì)介紹了c語(yǔ)言如何使用fdk_aac庫(kù)實(shí)現(xiàn)aac音頻解碼為pcm的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-11-11

最新評(píng)論