首页 > 解决方案 > 使用多个命名空间对 vtable 的未定义引用

问题描述

这几天我遇到了这个奇怪的链接问题。我有一个带有 2 个命名空间的 C++ 项目(在 Ubuntu 16.04 中)。每个命名空间在一个单独的目录中都有 .h 和 .cpp 文件,这些文件编译成一个库 .a 文件。最后,所有内容都链接到一个可执行文件中。

该项目非常大(OpenBTS的修改),所以为了更容易,这基本上就是我想要做的:

//*****directory A:

//file: A.h

namespace1 {

     class A {

         public:
         void functionA();
     };
}

//file: A.cpp

#include <B.h>
#include <vector.h>

using namespace1;
using namespace2;

void A::functionA (){
      B testB1;
      B testB2;
      testB2 = testB1; //works 
      testB1.functionB2(); //defined in B.h, works
      testB1.functionB(); //undefined reference to 'namespace2::B::functionB() const'
      std::vector<B> testvector;
      testvector.push_back(testB1); //undefined reference to 'namespace2::B'
}

//

//******directory B:

//file: B.h

#include "C.h"
//class C was part of the project before I started editing it and should not be the problem because other classes derived from it compile without problem.
namespace 2{
    class B: public C{
         int varB;
         public:
         B(){};
        ~B(){};

        void functionB() const; //overrides virtual functionB in class C
        int functionB2() { return varB;}
        void functionB2() //overrides pure virtual functionB2 in class C
    };
}

//file B.cpp

#include "B.h"
using namespace2

void B::functionB(){
  //code...
}

//main.cpp
//this creates an instance of A

最后,目录 A 中的所有文件都编译为 .o 文件,然后在库 Aa 中链接在一起,目录 B 也是如此。 main.cpp 也编译为 main.o

然后所有链接: g++ -g -O2 -Wall -pthread -rdynamic -o exename main.o ../B/.libs/Ba ../A/.libs/Aa -la53 -lzmq -pthread

这是我得到错误的地方:

对'namespace2::B'的未定义引用 对'namespace2::B::functionB() const'的未定义引用

我已经检查过 B 中是否覆盖了所有虚函数,这似乎没问题。此外,当我在 namespace2 的其他代码中使用 B 类时,没有问题,一切都编译得很好。调用 Bh 中定义的函数是有效的,所以看起来链接器无法访问 B.cpp 中定义的函数?

有什么建议么?

标签: c++linkerg++

解决方案


使用命名空间时,您应该将类​​方法的实现包装到命名空间以及 .cpp 文件中。您的 a.cpp 应该类似于:

//file: A.cpp

#include <B.h>
#include <vector.h>

namespace namespace1 {

using namespace2; // means 'look in namespace2' to the compiler

void A::functionA (){
      B testB1;
      B testB2;
      testB2 = testB1; 
      testB1.functionB2(); 
      testB1.functionB(); 
      std::vector<B> testvector;
      testvector.push_back(testB1); 
}

} // namespace1

你的 b.cpp 应该是这样的:

//file B.cpp

#include "B.h"
namespace namespace2 {

void B::functionB() const{
  //code...
}

} // namespace2

请注意,类型 B 的对象的实例化是有效的,因为构造函数是在类声明中定义的。对于B::function2(). 另一方面,A::functionA()andB::functionB()位于全局命名空间中,而不是应有的命名空间 1 和命名空间 2。

该子句using namespace_name;没有定义您的 cpp 文件中的范围。它只是通知编译器查看该名称空间以解析它将在该翻译单元中找到的符号。

尝试填充向量时出现的错误可能是由于您的类型缺少 的实现functionB(),因为它位于错误的(全局)命名空间中。因此类型 B 是不完整的,不能用于实例化模板类。

编辑:作为下面评论的后续,经过一些试验和错误,事实证明在代码方面一切都很好,但是由于 lib A 和 B 之间的循环依赖关系,链接失败,这在此中不清晰可见虚拟示例,并且由于需要正确的链接顺序才能工作的其他依赖项。

因此,对库的链接顺序进行简单更改是不可行的。我的建议是尽可能避免循环依赖,或者使用 ld 特定选项--start-group--end-group来解决此类问题。在与 ld 相关的gnu 手册页中了解更多信息。你会通过互联网找到很多关于它们使用的例子。

希望这可以帮助解决您的问题。


推荐阅读