2013-02-04 52 views
3

要使某個類的功能成爲一個插槽,該類必須從QObject繼承。但是,QObject佔用了相當大的內存。我不確定它是多少,以及內存是爲每個類還是每個對象。我的代碼有許多小數據,其功能可能是某個時隙的插槽。我想知道是否有一種方法可以在課堂上使用它時暫時使課堂功能成爲一個插槽。使用它之後,插槽成本的內存將被刪除。以下代碼說明了這一要求。如何使函數在Qt中暫時成爲一個槽?

class SmallData // size of 2 or 3 integers. 
{ 
public: 
    virtual void F(); // use it as a slot. 
    virtual QMenu* createMenu(); // use this to create the context menu with 
           // an action connected to F() 
    ... 
}; 

// use the small data 
vector<SmallData> vec(1000000); // the vector is put at a tree view. When an 
           // item in the tree view is selected, a context 
           // menu pop up with an action to run F(). 
SmallData* data = treeView.selectedItem();  
connect(action, SIGNAL(triggered()), data, SLOT(F())); // How to make F() to be 
                 // a slot just here. 
                 // The action is from 
                 // data->createMenu(). 
+0

是什麼讓你認爲QObject佔用了相當大的內存? – aschepler

+0

我檢查過,但不是很仔細。它有一個父指針和其他數據來實現它的機制。 – user1899020

+4

這是不可能的。此外,有一百萬個項目,使用基於項目的模型而不是定製的QAbstractItemModel將成爲您的瓶頸,而不僅僅是QObjects。添加一個插槽,查找選定的SmallData對象,在其上調用F()。 –

回答

8

如果你可以使用QT5,可以connect signals to plain functions and static methods(這基本上是一個重新命名好笑普通函數):

connect(action, &QAction::triggered, 
     &SmallData::statF); 

actionQAction實例,SmallData::statFSmallData一個靜態方法。每個基督徒勞的評論

編輯,調用特定情況下,你還可以連接到拉姆達:

connect(action, &QAction::triggered, 
     [data]() { data->F(); }); 

已經與Qt4的,你可以使用QSignalMapper實現幾乎相同的效果,多帶幾個對象。它允許你添加一個參數(在這種情況下,可能是一個整數索引到你的vec)來發送信號,根據哪個對象發射它。但在Qt4中,接收器仍然必須是QObject。

+0

如果這可行,這將是最簡單的解決方案。我的SmallData :: stateF是虛擬的而不是靜態的。任何方式來做到這一點? – user1899020

+1

+1哇,現在很好(還沒有看過這麼多新的Qt)。甚至可能與C++ 11的功能設施一起使用以避免靜態的東西:'connect(action,&QAction :: triggered,std :: bind(&SmallData :: F,data));'。如果這可行(他們至少說到支持'tr1 :: bind')並且可以添加它,那麼這就是恕我直言的最佳答案。 –

+0

@ user1899020不幸的是方法指針不被支持(據我所知)。如果你想調用特定對象實例的方法,它必須是一個QObject實例。 QSignalMapper允許您根據哪個QObject發出信號來傳遞differenet參數,這可能對您有幫助。 – hyde

3

對於使用信號插槽機制,您不會繞過QObject,但您可以做的是創建一個臨時對象,該對象具有一個調用您的函數的插槽。你只需要關心正確釋放對象。喜歡的東西:

class Callback : public QObject 
{ 
    Q_OBJECT 

public: 
    typedef std::function<void()> FunctionType; 

    Callback(FunctionType fn, bool oneShot = true, QObject *parent = nullptr) 
     : QObject(parent), fn_(std::move(fn)), oneShot_(oneShot) {} 

public slots: 
    void call() 
    { 
     fn_();  //delegate to callback 
     if(oneShot_) 
      deleteLater(); //not needed anymore 
    } 

private: 
    FunctionType fn_; 
    bool oneShot_; 
}; 

Callback* makeCallback(FunctionType fn, bool oneShot = true, QObject *parent = nullptr) 
{ 
    return new Callback(std::move(fn), oneShot, parent); 
} 

然後,您只需要創建一個(或多或少臨時)每次需要時Callback對象:

SmallData* data = treeView.selectedItem(); 
connect(action, SIGNAL(triggered()), 
     makeCallback(std::bind(&SmallData::F, data)), SLOT(call())); 

隨着oneShot參數,你可以控制,如果插槽應該解散後自動觸發。

唯一的問題是,如果這個插槽永遠不會被調用,那麼你會有一個漏洞Callback。爲了適應這一點,你可以通過一些有意義的事情到parent說法,使Qt的,至少在某個時間點關心正確的缺失:

SmallData* data = treeView.selectedItem(); 
connect(action, SIGNAL(triggered()), 
     makeCallback(std::bind(&SmallData::F, data), true, this), SLOT(call())); 

這種方式,您也可以綁定回調對象的生命週期(並因此將信號插槽連接到其他某個對象(例如,動作本身並在沒有選擇項目時刪除動作等)。

或者,您還可以記住當前選定項目的Callback對象,並在刪除後自行謹慎地刪除自己。

免責聲明:請注意,上面的例子包含大量的C++ 11,但我沒有心情重寫C++ 03。同樣,這個解決方案可以進一步提供進一步的印象,也許使用模板函數而不是std::function(但是如果我沒有記錯的話,Qt元對象系統不太喜歡模板)。


編輯:最終在他的評論被弗蘭克·奧斯特費爾德提出的解決方案可能是您的情況比我過於籠統對象的生命週期瘋狂上面一個更簡單的方法:只需將動作連接到單更高級別的對象(你的主要部件或可能包含數據載體的項目模型),並呼籲當前選擇的項目F插槽:

connect(action, SIGNAL(triggered()), this, SLOT(callF())); 
... 
void MyController::callF() 
{ 
    treeView.selectedItem()->F(); 
} 
相關問題