2012-02-14 82 views
1

我偶然发现了一个问题,我可以解决,但我不知道为什么它不起作用。朋友操作员重载导致“已定义在”链接器错误

这是我尝试使用的代码。为了简洁起见,字段已被删除。让我知道你在需要他们,我会把它们放回:

#pragma once 
#ifndef __PageStyle__ 
#define __PageStyle__ 
class PageStyle 
{ 
public: 
    friend bool operator<(const PageStyle& lhs, const PageStyle& rhs); 
}; 

bool operator<(const PageStyle& lhs, const PageStyle& rhs) 
{ 
    return (lhs.name < rhs.name); 
} 
#endif 

而在我的源文件,我做这样的事情:

#include "PageStyle.h" 

... 
void PageStyleManager::loadPageStyles() { 
    std::set<PageStyle> pageStyles; 
    ... 
} 

编译精细的代码,但链接争吵了这一点:

1>PageStyleManager.obj : error LNK2005: "bool __cdecl operator<(class PageStyle const &,class PageStyle const &)" ([email protected][email protected]@[email protected]) already defined in BaseContentFiller.obj 

BaseContentFiller为PageStyleManager基类,以及用于其它类以类似的方式也使用PageStyle。

经过深入研究,我发现为了我的目的(使用STL集合中的类),我真的不需要非成员的朋友版本。 我让操作员成为在线公众会员,并且代码链接没有问题

为什么会出现这个问题?我确保我使用了标头守卫,这是我第一次真正体验操作符重载,并且我想知道我做错了什么。

+1

除非它们是静态的,否则不应将非成员函数的*实现*放在头文件中。 – 2012-02-14 09:55:50

+2

两个无关的问题:'#pragma once'没有被普遍理解,并且在包含守卫之前拥有它可能会阻止某些替代技术的运行。如果你确实使用了'#pragma once',那就把它放在include guard后面。并且符号中的双下划线是未定义的行为,即使符号是包含警卫。 – 2012-02-14 10:42:12

回答

9

如果在头文件中包含该函数的定义,则会在包含头文件的每个Translation unit中定义它。
这违反了One Definition Rule并因此链接器错误。

请注意,标头防护或#pragma once只是防止同一个头文件多次包含在同一个源文件中,但不会在不同的TU中定义它。

两种解决方案来解决这个问题:

  1. 只需添加在头文件中的声明和定义只有一个cpp文件或
  2. 做在标题定义为inline功能。
+0

将声明移动到cpp文件工作,谢谢!我从字面上理解了各种在线教程(他们经常在页面上显示标题和源代码)。回想起来,在源文件中声明运算符是非常合理的。 – seanhodges 2012-02-14 12:16:07

6

不要在头文件中定义operator<;只要在那里声明它,并在.cpp文件中定义它。 #pragma once仅使头文件在同一个.cpp文件中多次包含,但它可能包含在不同的.cpp文件中,在这种情况下,链接器将看到operator<的多个定义。

+0

这个答案也很有用,谢谢Aasmund。我选择了另一个,只是因为链接到额外的资源。 – seanhodges 2012-02-14 12:17:20

+0

而不是标记你的答案解决,我已upvoted,所以你仍然得到你的10K代表:) – seanhodges 2012-02-14 12:22:12

+0

@seanhodges:不知何故,我觉得非常满意...:D – 2012-02-14 12:25:58