我不太确定我的问题在哪里。
我正在使用Qt Creator进行OpenGL项目,并且我第一次尝试在我的项目中添加另外几个类。我已经添加了header和implementation文件:terrain.h,terrain.cpp,imageloader.h和imageloader.cpp。
我认为我的问题出在我的.pro文件中:
HEADERS += glwidget.h \
imageloader.h \
terrain.h
SOURCES += glwidget.cpp main.cpp \
imageloader.cpp \
terrain.cpp
QT += opengl
CONFIG -= app_bundle
CONFIG += console c++11
INCLUDEPATH += "../include"
INCLUDEPATH += $$PWD
RESOURCES += shaders.qrc
当我运行qmake时,没有错误。
然后,当我构建项目时(清理后,运行qmake),我收到以下错误:
glwidget.obj:-1: error: LNK2019: unresolved external symbol "private: static class Terrain * __cdecl GLWidget::loadTerrain(char const *,float)" (?loadTerrain@GLWidget@@CAPEAVTerrain@@PEBDM@Z) referenced in function "protected: virtual void __cdecl GLWidget::initializeGL(void)" (?initializeGL@GLWidget@@MEAAXXZ)
Qt Creator还说:找不到文件glwidget.obj
然而,问题实际上似乎是地形类,或者它与glwidget的交互方式。
当我删除所有对terrain的引用时,构建工作正常。
但是当我在initializeGL()方法中将以下行添加到glwidget.cpp时,会出现LINK2019错误:
terrain = loadTerrain("test.bmp", 20);
terrain在glwidget.h中定义为terrain.cpp中的Terrain类的一个实例:
Terrain* terrain;
loadTerrain在glwidget.h中定义为:
static Terrain* loadTerrain(const char* filename, float height);
有趣的是,即使注释掉loadTeerain的实现,也会发生此错误。
以下是我尝试过的更多内容:
当我在qmake之前或之后运行clean时,或删除build文件夹中的.obj文件时,没有任何更改。我注意到重建项目时会出现glwidget.obj以及terrain.obj。
感谢任何帮助。如果您想查看我的任何文件,请告诉我。
编辑:即使将loadTerrain()方法设为非静态问题,问题仍然存在。
编辑2 我似乎也忘记了它投掷的第二个错误,但我不确定这会有什么帮助:
debug\program2.exe:-1: error: LNK1120: 1 unresolved externals
debug是出现.obj的文件夹,program2是项目的名称。
已修复!事实证明,将方法设为静态是个问题。您不能在头文件中声明一个静态方法,该方法将以我正在进行的方式由多个源使用。我换了:
static Terrain* loadTerrain(const char* filename, float height);
与
Terrain* GLWidget::loadTerrain(const char* filename, float height);
以及更改对方法的后续调用。
感谢您的帮助!
答案 0 :(得分:1)
通过创建函数static
,您可以使用内部链接。这意味着您的函数定义只能在定义它的翻译单元中找到(即terrain.cpp
)而不是main.cpp
。删除static关键字。
使用objdump
很容易确认。假设你有test.h
:
#ifndef _TEST_H
#define _TEST_H
static void a_func();
#endif
test.cpp
:
#include "test.h"
void a_func()
{
}
main.cpp
:
#include "test.h"
void a_func()
{
}
int main()
{
}
您会注意到您未获得功能重定义错误。我们可以单独编译每个文件来检查对象:
g++ -c test.cpp -o test.o
g++ -c main.cpp -o main.o
两者都会显示a_func
:
Disassembly of section .text:
0000000000000000 <_ZL6a_funcv>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 90 nop
5: 5d pop %rbp
6: c3 retq
现在最后的二进制文件呢?
g++ main.o test.o
objdump -d ./a.out
00000000004005b6 <_ZL6a_funcv>:
4005b6: 55 push %rbp
4005b7: 48 89 e5 mov %rsp,%rbp
4005ba: 90 nop
4005bb: 5d pop %rbp
4005bc: c3 retq
00000000004005bd <main>:
4005bd: 55 push %rbp
4005be: 48 89 e5 mov %rsp,%rbp
4005c1: b8 00 00 00 00 mov $0x0,%eax
4005c6: 5d pop %rbp
4005c7: c3 retq
00000000004005c8 <_ZL6a_funcv>:
4005c8: 55 push %rbp
4005c9: 48 89 e5 mov %rsp,%rbp
4005cc: 90 nop
4005cd: 5d pop %rbp
4005ce: c3 retq
4005cf: 90 nop
它出现两次!这就是为什么你不要在头文件中使函数静态,以便在多个翻译单元之间共享。
答案 1 :(得分:0)
似乎你在glwidget.cpp中缺少函数loadTerrain()
的实现:
Terrain* loadTerrain(const char* fileName, float height)
{
// your implementation here
}
您可以查看here样本。