C++ 使用命名空间生成全局函数,但获取多个定义的符号时出错
这些函数是我的大多数程序对象都会使用的实用类型的东西。我希望将它们放在名称空间中,并使它们成为全局的。此命名空间在头中定义,然后添加到我的预编译头中。但是到目前为止,我已经在两个不同的对象中使用了这个名称空间中的函数,并且编译器在这两个对象上抛出了multiply-defined-symbols错误 名称空间文件C++ 使用命名空间生成全局函数,但获取多个定义的符号时出错,c++,linker-errors,precompiled-headers,C++,Linker Errors,Precompiled Headers,这些函数是我的大多数程序对象都会使用的实用类型的东西。我希望将它们放在名称空间中,并使它们成为全局的。此命名空间在头中定义,然后添加到我的预编译头中。但是到目前为止,我已经在两个不同的对象中使用了这个名称空间中的函数,并且编译器在这两个对象上抛出了multiply-defined-symbols错误 名称空间文件 #ifndef UTILS_H #define UTILS_H #include <random> #include <cmath> namespace
#ifndef UTILS_H
#define UTILS_H
#include <random>
#include <cmath>
namespace Utils
{
extern int GetRandomBetween(int low, int high)
{
if (low < 0 || low >= high)
return 0;
int seed = high - low;
return (rand() % seed) + low;
}
};
#endif
\ifndef UTILS\u H
#定义UTILS\u H
#包括
#包括
命名空间Utils
{
外部int GetRandomBetween(int低,int高)
{
如果(低<0 | |低>=高)
返回0;
int seed=高-低;
返回值(rand()%seed)+低;
}
};
#恩迪夫
和我的预编译头
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <random>
#define SAFE_DELETE( p ) { if( p ) { delete ( p ); ( p ) = NULL; } }
#define SAFE_DELETE_ARRAY( p ) { if( p ) { delete[] ( p ); ( p ) = NULL; } }
#define SAFE_RELEASE( p ) { if( p ) { ( p )->Release(); ( p ) = NULL; } }
// TODO: reference additional headers your program requires here
#include "Utils.h"
#include "Manager.h" // this object uses utils
#include "Bot.h" // this object uses utils
#include "LinkedList.h"
#include "Village.h" // this object will use utils in the future
//stdafx.h:标准系统包含文件的包含文件,
//或特定于项目的包含经常使用的文件,但
//变化不大
//
#布拉格语一次
#包括“targetver.h”
//#定义WIN32_LEAN_和_MEAN//从Windows标题中排除很少使用的内容
//Windows头文件:
#包括
//C运行时头文件
#包括
#包括
#包括
#包括
#包括
#定义SAFE_DELETE(p){if(p){DELETE(p);(p)=NULL;}}
#定义安全删除数组(p){if(p){DELETE[](p);(p)=NULL;}
#定义安全释放(p){if(p){(p)->RELEASE();(p)=NULL;}}
//TODO:在此处引用程序所需的其他标题
#包括“Utils.h”
#包括“Manager.h”//此对象使用UTIL
#包括“Bot.h”//此对象使用UTIL
#包括“LinkedList.h”
#包括“Village.h”//此对象将来将使用UTIL
链接器错误消息:
Manager.obj:错误LNK2005:“int u cdecl Utils::GetRandomBetween(int,int)”(?GetRandomBetween@Utils@@YAHHH@Z)已在Bot.obj中定义
stdafx.obj:错误LNK2005:“int\uu cdecl Utils::GetRandomBetween(int,int)”(?GetRandomBetween@Utils@@YAHHH@Z)已在Bot.obj中定义
c:\users\lee\documents\visualstudio 2010\Projects\AI\Debug\AI.exe:致命错误LNK1169:找到一个或多个多重定义符号
也许值得注意的是,在我的Manager类标题中,我转发了声明的Bot。与村级标题相同。Manager.cpp
和Bot.cpp
都包括Util.h
因此,在编译它们时,两个对象文件都会导出符号“GetRandomBetween”。当链接器将这些对象文件合并到一个可执行文件中时,它会找到该函数的两个实例。链接器无法确定使用哪一个(并且它不理解它们是相同的)
如果要使对象文件不导出符号(以便不存在链接器冲突),请删除extern关键字。函数定义(即:源代码)不应位于标题中。获取多个定义的原因是extern
无法将函数定义(源代码)转换为函数声明(即:仅原型)。因此,您需要这样做:
Util.h:
namespace Utils
{
int GetRandomBetween(int low, int high);
};
SomeSourceFile.cpp(可能是Util.cpp):
尽管您应该只在小功能中使用它。没有它就无法工作。我首先写的时候没有使用extern关键字,这就是我得到错误并添加exern的时候。但我还是继续尝试了一次,没有使用extern,但仍然不能产生相同的错误。@J的分析是正确的,但他的建议是错误的。您有两种选择:要么仅在标题中声明函数,并将定义移动到CPP文件,要么将extern
替换为inline
。好的,有三种选择——您可以用静态
替换外部
,但不能<代码>内联
将比静态
更少浪费。完美!谢谢你们,你们俩的代表。啊,你们救了我一天<代码>内联
工作起来很有魅力!但是@Rob,既然我们在做,为什么内联
比静态
浪费更少呢?为什么这些选项中的任何一个都是浪费的?@SexyBeast static和inline将创建fn的两个副本,但它们将仅对它们在(a.obj,b.obj)中定义的翻译单元进行访问-这就是此解决方案解决错误的原因。两者之间的区别在于内联将在调用它的地方嵌入代码,而静态将添加跳转汇编指令并生成堆栈帧。帧和跳转在运行时代价很高,但会创建一个更压缩的二进制文件。内联方法将增加二进制但优化的运行时成本。现代的编译器会忽略这两者,并用它认为最好的方法优化代码。
namespace Utils
{
int GetRandomBetween(int low, int high);
{
if (low < 0 || low >= high)
return 0;
int seed = high - low;
return (rand() % seed) + low;
}
};
namespace Utils
{
inline int GetRandomBetween(int low, int high)
{
if (low < 0 || low >= high)
return 0;
int seed = high - low;
return (rand() % seed) + low;
}
};