2012-08-15 55 views
1

我希望有一些包装代码是一个只有标题的库。我受到boost库的启发,只是为了简化分发.lib和包括编译.cpp的需要。重构导致链接器问题与重复

在这个例子中,“z.h”是包装器,而a.cpp已被重构为将B()移入它自己的源文件中。现在它不起作用。

ZH

class Z 
{ 
    public: 
    void Foo(); // edited to match my code 
}; 

Z::Foo() { } 

a.cpp

#include "z.h" 
void A() { 
     Z z; 
     z.Foo(); 
} 
//void B(Z z) { 
//  z.Foo(); 
//} 

b.cpp

#include "z.h" 
void B(Z z) {   
     z.Foo(); 
} 

*错误LNK2005: “市民:__cdecl无效ž::富()” 已定义在b.obj *

我知道我可以修复t他通过将z.h分为z.h和z.cpp来定义声明。

  • 但是,如何让Boost库在没有.cpp文件的情况下离开?
  • 一切都需要成为模板吗?
  • 什么代码可以在z.h中进行?
+0

你有没有在你的'.h'文件中包含警卫? – juanchopanza 2012-08-15 21:28:57

+0

当然,我有。包括卫兵只会帮助编译,而不是链接。 (至少在视觉工作室中)。 – 2012-08-15 21:30:28

+3

这段代码没问题。你的* real *代码是什么样的?也许在类声明的外部实现Foo * *而不标记它内联? – 2012-08-15 21:32:47

回答

2

一时间,考虑这个标题:

// foo.hpp 

void foo(int x) 
{ 
    /* do something */ 
} 

(头警卫没有相应和在这里,他们每个翻译单位工作)。我们现在有两个翻译单元:

// a.cpp 

#include "foo.hpp" 

和:

// b.cpp 

#include "foo.hpp" 

什么这最终做的是定义foo(int x)两次,每个TU一次。根据一种定义规则(ODR),不允许使用多种定义,尽管从技术上讲不需要诊断,但在编译器部分这样做很微不足道,因此会出现错误。

幸运的是,有一个关键词,inline,这改变了这种行为:

// foo.hpp 

inline void foo(int x) 
{ 
    /* do something */ 
} 

这个关键字告诉的是,它应该在多个定义链接器,它可以自由地选择一个单一的定义,并丢弃休息。 (这取决于你确保这实际上是可以的!)通过这一改变,解决了以前的ODR违规问题,并且程序进行了编译。现在

,你所列出的标题为:

class Z  
{  
    public:  
    void Foo(){  
     //do stuff  
    }  
};  

这是相同的:

class Z  
{  
    public:  
    void Foo(); 
};  

inline void Z::Foo(){  
     //do stuff  
    } 

,因为在类中定义的函数隐inline。 (这允许您将定义包含在多个翻译单位中,而不会出错。)我怀疑你写什么不是什么是在您的文章,但这样的事情:

class Z  
{  
    public:  
    void Foo(); 
};  

void Z::Foo(){  
     //do stuff  
    } 

其中inline丢失。等效性丢失,创建了多个定义,并且违反了ODR。

解决方法是使用inline,或者通过定义类定义中的函数来隐式使用它。就我个人而言,我发现后者更清晰(因为你避免重复自己),并且更容易维护。 Boost经常使用inline来避免单一定义的需要。

+0

这听起来很好解释。我只需要看看这是否是我想要的效果。理想情况下,我希望它通过不内联与以前相同。我试图用** extern **找到一个模式。 – 2012-08-16 14:04:07

+0

@BenL:'inline'并不意味着函数被内联,这将总是由编译器决定,不管它与否。该功能仍然具有外部连接。内部联系将是“静态的”。 – GManNickG 2012-08-16 14:30:43