2011-11-29 85 views
1

此代碼編譯,鏈接和按預期工作:爲什麼如果在.cpp文件中有Q_OBJECT宏,我的項目不會鏈接?

#include <QApplication> 
#include <QListView> 
#include "File_List_Model.h" 

int main(int c,char**v) 
{ 
    QApplication app(c,v); 
    QStringList list; 

    list << "a" << "b" << "c"; 

    File_List_Model* model = new File_List_Model; 
    model->set_entries(list); 

    QListView* view = new QListView; 
    view->setModel(model); 
    view->show(); 

    return app.exec(); 
} 

但是,當我把類定義.cpp文件,而不是頭文件,我得到的鏈接錯誤,指出vtable沒有正確定義。

#include <QApplication> 
#include <QListView> 
//#include "File_List_Model.h" 
#include "File_List_Proxy.h" 
#include <QAbstractItemModel> 
#include <QStringList> 

class File_List_Model : public QAbstractItemModel 
{ 
    Q_OBJECT 
private: 
    QStringList data_; 
public: 
    File_List_Model(QObject *parent = 0) : 
     QAbstractItemModel(parent) 
    { 
    } 

    int columnCount(const QModelIndex& parent) const 
    { 
     return 1; 
    } 

    QVariant data(const QModelIndex & index, int role) const 
    { 
     switch(role) 
     { 
      case Qt::DisplayRole: 
      return data_[index.row()]; 
     default: 
      return QVariant(); 
     } 
    } 

    QModelIndex index(int row, int column, const QModelIndex & parent) const 
    { 
     return createIndex(row,column); 
    } 

    QModelIndex parent(const QModelIndex & index) const 
    { 
     return QModelIndex(); 
    } 

    bool set_entries(const QStringList& entries) 
    { 
     if (entries.size()) 
     { 
     beginInsertRows(createIndex(0,0),0,entries.size()); 
     data_ = entries; 
     endInsertRows(); 
     emit dataChanged(createIndex(0,0),createIndex(0,entries.size())); 
     return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 

    int rowCount(const QModelIndex & parent) const 
    { 
     return data_.size(); 
    } 


}; 

int main(int c,char**v) 
{ 
    QApplication app(c,v); 
    QStringList list; 

    list << "a" << "b" << "c"; 

    File_List_Model* model = new File_List_Model; 
    model->set_entries(list); 

    File_List_Proxy* proxy = new File_List_Proxy; 
    proxy->setSourceModel(model); 

    QListView* view = new QListView; 
    view->setModel(proxy); 
    view->show(); 

    return app.exec(); 
} 
//error: 
debug/moc_File_List_Model.o:moc_File_List_Model.cpp:(.rdata$_ZTV15File_List_Model[vtable for File_List_Model]+0x44): undefined reference to `File_List_Model::columnCount(QModelIndex const&) const' 
debug/moc_File_List_Model.o:moc_File_List_Model.cpp:(.rdata$_ZTV15File_List_Model[vtable for File_List_Model]+0x4c): undefined reference to `File_List_Model::data(QModelIndex const&, int) const' 

這似乎是完全一樣的代碼。爲什麼當代碼位於標題中時它會鏈接,並且它不會鏈接到其他位置?

回答

3

Qt使用moc工具來處理所需的C++擴展,例如對於信號插槽機制。此工具處理項目中的所有標題(!)文件,並生成新的源文件,其中包含對包含Q_OBJECT宏的類的元對象代碼。

當您在.cpp文件中定義您的類而不是.h文件moc無法正確處理它。

有關Qt元對象編譯器的更多信息,請看this article

+0

qmake和cmake Qt支持在項目中列出的包含'Q_OBJECT'宏的所有源文件上調用moc。無論文件的擴展名如何,都會發生這種情況。在這個問題的情況下,moc輸出沒有傳遞給編譯器,因爲qmake/cmake不能自動執行。 –

0

因爲Qt在頭文件上運行moc並且不在源文件上運行它。

+0

這是錯誤的:qmake和cmake在包含'Q_OBJECT'宏的所有源文件上運行moc。這裏沒有做的是在moc的輸出上運行編譯器。 –

-1

鏈接器抱怨缺少來自編譯moc輸出的目標代碼。這是因爲儘管moc已經處理了源文件,但它的輸出沒有被編譯到一個目標文件中。

對於頭文件,構建系統假定它們意味着包含在多個翻譯單元中,並且不會違反one definition rule。因此,moc輸出可以包含頭文件,並作爲獨立的翻譯單元進行編譯。

但是,如果你有一個.cpp文件中的任何Q_OBJECT宏,商務部輸出不能孤立地編譯:它不會訪問聲明從.cpp文件,因此無法編譯!它也不能包含您的.cpp文件,因爲這會違反one definition rule:兩個翻譯單元 - moc輸出和您的文件 - 將定義相同的內容。

相反,您需要將moc的輸出附加到.cpp文件的末尾。例如,如果你在main.cppO_OBJECT,在文件的最後添加#include "main.moc"

// main.cpp 
#include <QtCore> 

struct Object : QObject { 
    Q_OBJECT 
}; 

int main() { 
    Object o; 
    qDebug() << o.metaObject()->className(); 
} 

#include "main.moc" 
// "main.moc" depends on the declaration of Object above! 

以上是SSCCE

有人可能會爭辯說,也許qmake/cmake應該建立構建,以便moc輸出在發送到編譯器之前自動附加到.cpp文件。到目前爲止,該功能尚未實現。

相關問題