struct X
{
virtual void foo();
};
struct Y : X
{
void foo() {}
};
struct A
{
virtual ~A() = 0;
};
struct B: A
{
virtual ~B(){}
};
extern int x;
void foo();
int main()
{
x = 0;
foo();
Y y;
B b;
}
GCC会产生如下错误:
/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status
4条答案
按热度按时间sbtkgmzw1#
声明但未定义变量或函数。
典型的变量声明是
由于这只是一个声明,需要单一定义。相应的定义为:
例如,以下代码将生成错误:
类似的评论也适用于函数。在未定义函数的情况下声明该函数会导致错误:
注意,您实现的函数与您声明的函数完全匹配。例如,您的简历限定符可能不匹配:
其他不匹配的例子包括
来自编译器的错误消息通常会给出已声明但从未定义的变量或函数的完整声明。将其与您提供的定义进行密切比较。确保每个细节都匹配。
7y4bm7vi2#
编译C++程序分几个步骤进行,如2.2(credits to Keith Thompson for the reference)所指定:
翻译的语法规则之间的优先顺序由以下阶段*[见脚注]*规定。
1.如有必要,以实现定义的方式将物理源文件字符Map到基本源字符集(引入换行符作为行尾指示符)。[剪报]
1.删除紧跟换行符的反斜杠字符()的每个示例,拼接物理源行以形成逻辑源行。[剪报]
1.源文件被分解为预处理标记(2.5)和空白字符序列(包括注解)。[剪报]
1.执行预处理指令,展开宏调用,执行_Pragma一元运算符表达式。[剪报]
1.将字符文字或字符串文字中的每个源字符集成员,以及字符文字或非原始字符串文字中的每个转义序列和通用字符名称转换为执行字符集的相应成员;[Snip]
1.连接相邻的字符串文字标记。
1.空格字符分隔令牌不再重要。每个预处理令牌被转换为令牌。(2.7)。得到的标记被作为翻译单位进行句法和语义分析和翻译。[剪报]
1.翻译后的翻译单元和示例化单元组合如下:[Snip]
1.解析所有外部实体引用。链接库组件以满足对未在当前转换中定义的实体的外部参照。所有这样的翻译器输出都被收集到一个程序映像中,其中包含在其执行环境中执行所需的信息。(重点挖掘)
指定的错误发生在编译的最后阶段,通常称为链接。这基本上意味着您将一组实现文件编译成目标文件或库,现在您想让它们协同工作。
假设您在
a.cpp
中定义了符号a
。现在,b.cpp
声明了该符号并使用了它。在链接之前,它简单地假设该符号是在某个地方定义的,但它并不关心在哪里定义的。链接阶段负责查找符号并将其正确链接到b.cpp
(实际上是链接到使用它的对象或库)。如果您使用的是Microsoft Visual Studio,您将看到项目生成
.lib
文件。其中包含一个导出符号表和一个导入符号表。导入的符号将根据链接所针对的库进行解析,并为使用该.lib
(如果有)的库提供导出的符号。对于其他编译器/平台也存在类似的机制。
常见的错误信息有:Microsoft Visual Studio的
error LNK2001
、error LNK1120
、error LNK2019
;GCC的undefined reference to
symbol Name。代码:
GCC会产生如下错误:
Microsoft Visual Studio也有类似的错误:
常见原因包括:
#pragma
(Microsoft Visual Studio)UNICODE
definitionsr6l8ljro3#
类成员:
纯
virtual
析构函数需要实现。将析构函数声明为纯函数仍然需要您定义它(与常规函数不同):
这是因为在隐式销毁对象时会调用基类析构函数,因此需要定义。
virtual
方法必须实现或定义为纯方法。这类似于没有定义的非
virtual
方法,但增加了一个推理,即纯声明生成一个虚拟vtable,并且您可能在不使用函数的情况下获得链接器错误:为此,请将
X::foo()
声明为纯:非
virtual
类成员有些成员即使没有显式使用,也需要定义:
以下内容将产生错误:
在类定义本身中,实现可以内联:
或室外:
如果实现在类定义之外,但在头中,则必须将方法标记为
inline
,以防止多重定义。如果使用,则需要定义所有使用的成员方法。
一个常见的错误是忘记对名称进行限定:
其定义应为
static
数据成员必须在类外定义单个翻译单元:可以为类定义中整型或枚举型的
static``const
数据成员提供初始值设定项;然而,ODR使用该成员仍然需要如上所述的命名空间范围定义。C++11允许在类内对所有static const
数据成员进行初始化。slmsl1lt4#
链接对应的库/对象文件或编译实现文件失败
通常,每个翻译单元将生成一个目标文件,其中包含在该翻译单元中定义的符号的定义。要使用这些符号,您必须链接到这些目标文件。
在GCC下,您可以指定要在命令行中链接在一起的所有目标文件,或者一起编译实现文件。
-l...
必须位于任何.o
/.c
/.cpp
文件的右侧。这里的
libraryName
只是库的简单名称,没有特定于平台的附加内容。例如,在Linux上,库文件通常称为libfoo.so
,但您只需编写-lfoo
。在Windows上,相同的文件可能被称为foo.lib
,但您将使用相同的参数。您可能需要使用-L‹directory›
添加可以找到这些文件的目录。确保在-l
或-L
之后不写入空格。对于XCode:添加用户头搜索路径->添加库搜索路径->将实际的库引用拖放到项目文件夹中。
在MSVS下,添加到工程中的文件会自动将其目标文件链接在一起,并生成一个
lib
文件(常用)。要在单独的项目中使用符号,需要在项目设置中包括lib
文件。这是在Input -> Additional Dependencies
中的项目属性的Linker部分中完成的。(应该在Linker -> General -> Additional Library Directories
中添加lib
文件的路径)使用随lib
文件提供的第三方库时,如果不这样做,通常会导致错误。还可能发生忘记将文件添加到编译中的情况,在这种情况下,不会生成目标文件。在GCC中,将文件添加到命令行。在MSVS中,将文件添加到项目中将使其自动编译(尽管文件可以手动从构建中单独排除)。
在Windows编程中,未链接所需库的标志是未解析符号的名称以
__imp_
开头。在文档中查找函数的名称,它应该说明您需要使用哪个库。例如,MSDN将信息放在每个函数底部名为“库”的部分中的一个框中。