【问题标题】:Linking error when the class has the function defined inside类具有内部定义的函数时的链接错误
【发布时间】:2020-11-03 20:49:05
【问题描述】:

f1.cpp 文件包含两个简单类的代码。 A 类内部定义函数print()B 类外部定义函数。

class A{
public:
    void print(){}
};

class B{
public:
    void print();
};

void B::print(){
}

f1.cpp 将生成f1.lib

f2.cpp 包含两个类的标头和main() 函数(它将与f1.lib 链接以创建f2.exe):

class A{
public:
    void print();
};

class B{
public:
    void print();
};

int main(){
    A a;
    a.print();

    B b;
    b.print();
}

当我编译时(在 Visual Studio 2019 中),我只收到类 A 的链接错误:

error LNK2001: unresolved external symbol "public: void __thiscall A::print(void)" (?print@A@@QAEXXZ)
fatal error LNK1120: 1 unresolved externals

似乎A::print 函数没有作为符号出现在库中。

最初我认为这是因为默认情况下内部函数定义变为“内联”。但是我已经尝试在它前面添加__declspec(noinline) 属性,它仍然无法正常工作。

你知道为什么符号不存在吗?

【问题讨论】:

    标签: c++ linker inline


    【解决方案1】:

    f2.cpp 重新声明了AB,为什么?

    两个文件中的B 声明匹配,这没关系,链接器会将它们与f1.cpp 中的外部B::print() 定义匹配。

    但是两个文件中的A 声明不匹配,这是不行的,并且是未定义的行为f1.cpp 中的Aprint() 有自己独特的内联定义,但f2.cpp 中的Aprint() 有一个非内联声明,并且没有匹配的外部定义供链接器查找,因此错误。

    f2.cpp 根本不应该重新声明 AB。改用头文件,例如:

    // f1.h
    
    #ifndef F1_H
    #define F1_H
    
    class A{
    public:
        void print() { ... }
        // or: void print();
    };
    
    #endif
    
    // f1.cpp (only if A::print() is not inline)
    
    #include "f1.h"
    
    void A::print() {
        ...
    }
    
    // f2.h
    
    #ifndef F2_H
    #define F2_H
    
    class B{
    public:
        void print();
    };
    
    #endif
    
    //f2.cpp
    
    #include "f1.h"
    #include "f2.h"
    
    void B::print() {
        ...
    }
    
    int main() {
        A a;
        a.print();
    
        B b;
        b.print();
    }
    

    或者:

    // f1.h
    
    #ifndef F1_H
    #define F1_H
    
    class A{
    public:
        void print() { ... }
        // or: void print();
    };
    
    class B{
    public:
        void print();
    };
    
    #endif
    
    //f2.cpp
    
    #include "f1.h"
    
    // uncomment if A::print() is not inline...
    /*
    void A::print() {
        ...
    }
    */
    
    void B::print() {
        ...
    }
    
    int main() {
        A a;
        a.print();
    
        B b;
        b.print();
    }
    

    【讨论】:

    • 没错。如果你重新声明了隐藏头文件中声明的类,编译器如何在头文件中找到方法的定义
    • @AyratArifullin 编译器 没有找到定义。 链接器可以。
    • 与上述答案相同的评论:我不明白为什么这两个声明不匹配。我认为在类内或类外定义一个函数只是一个选择,它与它的编译方式无关(如果我们排除“内联”部分 - 我试图强制排除“__declspec(noinline) 属性”。
    • @CatalinPanait 想一想如果具有内联方法定义的类在 2 个单独的翻译单元中声明会发生什么。两个单独的实现,除非链接器一起优化它们。因此,在链接器阶段(编译器阶段不知道更好),内联定义的处理方式与外部定义不同是有道理的。
    【解决方案2】:

    你知道为什么符号不存在吗?

    您有 2 个不同的类 A 声明,这会导致未定义的行为。这些声明必须匹配。您可以通过修复第二个声明使其工作,但更简单和常见的方法是将该声明放入头文件中,并将 #include 放入两个 .cpp 文件中(如果它们使用声明的类,则更多)。

    【讨论】:

    • 我不明白为什么这两个声明不匹配。我认为在类内或类外定义一个函数只是一个选择,它与它的编译方式无关(如果我们排除“内联”部分 - 我试图强制排除“__declspec(noinline) 属性”。
    • @CatalinPanait “我不明白为什么这两个声明不匹配。”因为它们不同,但它们必须相同。是否将方法放入类声明中是一种选择,但您不能为同一个类多次做出该选择,所有编译单元(即 cpp 文件)的类定义必须相同,这就是语言所说的。
    猜你喜欢
    • 2018-12-06
    • 2011-01-24
    • 1970-01-01
    • 1970-01-01
    • 2016-08-25
    • 2012-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多