1. 程式人生 > 實用技巧 >【轉載】QtCreator架構解析

【轉載】QtCreator架構解析

目錄

cnblog: qt creator原始碼全方面分析(4)

1. Qt框架的基本構件

1.1. Qt巨集

  • Q_EMIT,Q_SIGNALS和Q_SLOTS

    避免與第三方的emit,signal和slots衝突

  • Q_ENUM和Q_ENUM_NS

    向元物件系統註冊enum

  • Q_FLAG和Q_FLAG_NS,配合Q_DECLARE_FLAGS

    宣告enum,並向元物件系統註冊enum,用於flag的OR操作聯合

  • Q_INTERFACES

    一般實現外掛用,告訴Qt實現了哪些介面。

  • Q_INVOKABLE

    向元物件系統註冊method,後續可被元物件呼叫

  • Q_NAMESPACE

    向名稱空間新增QMetaObject

  • Q_OBJECT

    元物件的前提

  • Q_PROPERTY

    向元物件註冊屬性

1.2. d指標和q指標

d指標和q指標

這是一個絕妙的技巧,能夠在不破壞二進位制相容性的情況下將新的私有資料成員新增到類中。此外,它還能保持標頭檔案的乾淨,並隱藏具體的實現,加速編譯。

1.3. global標頭檔案

外掛的本質就是動態連結庫,對於庫,需要匯出符號,供使用者匯入使用。在qt creator的原始碼中,存在固定的匯入匯出模式。

對於每個庫和外掛,都有一個 xx_global.h 標頭檔案,其中xx為庫或外掛名。示例如下

#pragma once

#include <qglobal.h>

#if defined(XX_LIBRARY)
#  define XX_EXPORT Q_DECL_EXPORT
#else
#  define XX_EXPORT Q_DECL_IMPORT
#endif

這就很明顯了,XX_LIBRARY作為開關,來決定匯入匯出。

對於具體的實現,我們可以

class XX_EXPORT xx { ... }

在類的宣告中新增 XX_EXPORT

在qt creator原始碼中,在專案檔案中添加了定義

DEFINES += XX_LIBRARY

這導致 XX_EXPORT 被替換為 Q_DECL_EXPORT 進行匯出。

對於庫或外掛的使用者,直接包含xx.h即可,由於沒有定義XX_LIBRARY,這裡XX_EXPORT被替換為Q_DECL_IMPORT進行匯入。

1.4. 內外名稱空間

QtCreator原始碼中,每一個子專案都有內外兩層名稱空間,一個是外部的,一個是內部的。

示例如下

namespace ExtensionSystem {

namespace Internal {
    class IPluginPrivate;
    class PluginSpecPrivate;
}

class EXTENSIONSYSTEM_EXPORT IPlugin : public QObject
{
    ...
};

} // namespace ExtensionSystem
  • ExtensionSystem是外部的,其中的類等內容會被EXTENSIONSYSTEM_EXPORT匯出,使用者使用時可見。
  • ExtensionSystem::Internal是內部的,定義的都是內部私有類(參考公有私有類),不進行匯出。譬如前置宣告中的IPluginPrivate。

1.5. 統計介面實現

我們知道,外掛架構必不可少的是定義介面類,即抽象基類,描述使用者需要自定義實現的內容。此外,一般還有一個管理器類,對介面類的所有實現類進行管理,並呼叫其中的介面進行。

原始碼中有兩種方式來獲取所有實現類。

// 介面類宣告 xx.h
class ISimple
{
    ISimple();
    ~ISimple();
    virtual void algo1() = 0;
};

// 管理類宣告 xxmanager.h
class SimpleManager
{
    static void registerSimple(const ISimple *obj);
}

// xxmanager.cpp實現檔案中

// 統計介面的所有實現
static QList<ISimple *> g_simples;

// 方式1
ISimple::ISimple()
{
    g_simples.append(this);
}

ISimple::~ISimple()
{
    g_simples.removeOne(this);
}

// 方式2
void SimpleManager::registerSimple(const ISimple *obj)
{
    g_simples.append(obj);
}

一般,我們會把所有實現的列表g_simples,放在管理類中用於管理。

  • 方式1中介面類建構函式中添加了統計功能。
  • 方式2中管理類中添加了註冊函式,用於統計。

一般我們在IPlugin的 initialize() 函式中建立物件,那麼在呼叫完所有外掛的 initialize() 函式後,我們可以認為已經完成了統計,就可以在 extensionsInitialized() 中使用g_simples了。

1.6. QLatinString

Qt中處理字串最常用的肯定是QString,但是在qt creator原始碼中出現了大量的QLatin1String。下面我們來介紹下區別。

這是一個巨集,在編譯時從字串文字str中為QString生成資料。 在這種情況下,可以免費建立QString,並且將生成的字串資料儲存在已編譯目標檔案的只讀段中。

如果您的程式碼如下所示:

  // hasAttribute takes a QString argument
  if (node.hasAttribute("http-contents-length")) //...

然後這將建立一個臨時QString作為hasAttribute函式引數進行傳遞。 這可能會非常昂貴,因為它涉及記憶體分配以及將資料複製/轉換為QString的內部編碼。

通過使用QStringLiteral可以避免此成本:

if (node.hasAttribute(QStringLiteral(u"http-contents-length"))) //...

在這種情況下,QString的內部資料將在編譯時生成。在執行時不會發生任何轉換或分配。

使用QStringLiteral而不是用雙引號引起來的純C++字串文字,可以顯著加快根據編譯時已知的資料建立QString例項的速度。

注意:當將字串傳遞給具有過載QLatin1String引數的函式時,QLatin1String仍比QStringLiteral更有效,並且此過載避免了轉換為QString。例如,QString::operator ==() 可以直接與 QLatin1String 進行比較:

if (attribute.name() == QLatin1String("http-contents-length")) //...

注意:某些編譯器編碼包含US-ASCII字符集以外字元的字串會有bug。在這種情況下,請確保在字串前加上u。否則是可選的。

說白了,QStringLiteral在編譯期就建立了資料,避免了記憶體分配,加速了QString的建立。

1.7. Qt外掛

  1. How to Create Qt Plugins
  2. - Defining Plugins
  3. Echo Plugin Example
  4. QPluginLoader
  5. QLibrary
  6. moc(原始碼見qtbase/src/tools/moc目錄)