找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 4479|回复: 0

vs:有关某变量已经在???.obj中定义的错误的解决

[复制链接]

307

主题

228

回帖

7349

积分

用户组: 真·技术宅

UID
2
精华
76
威望
291 点
宅币
5599 个
贡献
253 次
宅之契约
0 份
在线时间
949 小时
注册时间
2014-1-25
发表于 2014-6-15 14:46:49 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
vs:有关某变量已经在???.obj中定义的错误的解决以前在qq空间发过这么一篇文章:我是这么说的:        该问题应该很多菜鸟和高手都会遇到。虽然我已精通c,但是仍时不时受制于编译器,有些错误的确是疏忽或者没休息好,太着急写代码导致的。而有些则是编译器和语法与生俱来的问题了,前2天还遇到了类模板实现和定义分离的问题折麽。语法这种东西,我认为是由于技术限制无法解决的逻辑矛盾和分析矛盾,因此我认为他们最终给都可以被智能的编译器消除,语法就是人造的,半逻辑的东西,纯粹追求语法无意义,编译器更新总是更好的,想起来最开始的编译器给的限制真是多,比如很早的时候printf不能自己处理16位32位整数,书上还给出了“正确”做法,不过现在编译器早不会有这种bug了。
         回归正题,今天编程时遇到了某变量已经在???.obj中定义的错误(都可以说是bug了,好恶心),这个错误原因编译器已经说得很明确,就是重复包含变量的定义了,该变量在某头文件里声明和定义,被多个cpp包含,就会出问题,1个cpp倒是好解决的。看遍网上说法,经典的做法是不在h文件定义全局变量,或者把h文件内容移到cpp,这个有点绝,因为我的工程里的2个cpp就是需要重复使用头文件,放出来岂不是定义2次,太麻烦不喜欢用,因此我开始自己探索如果破除编译器的限制,摸索出能编译通过的方法,纠正编译器的坏毛病!(说白了还是不够智能)。这期间,我试过#pragma once    #ifdef#endif 这些我都知道怎么用,不过解决不了问题,网上的回答水平还是比较初级的,自己摸索吧。网上有人说这是个初级问题,初学者才犯,不过也没提出什么高雅的解决方法,是不是高手还是要看会不会解决问题的,问题谁都会碰到,看到有很多所谓高手遇到这种问题不也是只能用下下等方法解决。最终摸索出来如下方法(过程就不说了),且摸索出原理是:编译器在头文件遇到变量定义声明时,会直接连接到obj中,此时你在同一个工程的不同cpp里多次包含同一个头文件,编译器就会说重复定义了这个变量,通俗点说法就是这些cpp共同分享了这个变量;而遇到类型定义,比如定义一个类,那么你在各个不同的cpp里都包含多次都是没什么问题的,也就是说类型定义是不在cpp之间共享的。
        但是你不要断章取义,在同一个cpp里多次无意的包含了同一个h文件会提示重定义,此问题可以通过#ifdef 解决,而我说的是不同cpp文件各自包含一次h,这样是没问题的。这只是h文件仅含类型定义的时候啊,别搞错了,如果h文件仅含有变量定义声明,那么整个工程只能有一个cpp包含这个h,其他cpp若想使用则只需用extern 引入该变量即可。如果有第二个cpp包含该文件,则编译器会提示某变量已经在x.obj文件中定义了,你会发现x.cpp包含了该h。
         
        首先,作出如下假设:你有一个1.h需要重复包含,其中有类型定义和全局变量,同时有1.cpp 2.cpp .....主函数在哪里不重要,再假设没有头文件重复包含的情况(该情况也没什么,无非在看懂本文后自己多添几行#ifdef了)。为了简单起见,假设1.h文件内容如下:
#include <windows.h>
class my
{
int i;
};
BOOL (WINAPI* closehandle)(HANDLE hObject)=CloseHandle;
int i=0;

3个cpp分别对应3种可能情况:
假设3.cpp内容如下:3.cpp只用到了变量i
void func1()
{
i=1;//这个i就是想用的头文件定义的i了
}
假设2.cpp内容如下:2.cpp用到了变量i和类类型my
void func()
{
i=1;
my me;
}
假设1.cpp内容如下:1.cpp只用到了类类型my
void main()
{
my me;   
]

上面是一个类my的定义,下面是变量closehandle和i的定义了
        此时我们需要把类型和变量分开,始终要记得类型可以重复包含(别断章取义),变量只能包含一次。为此设置2个预编译宏做区分,修改1.h为如下所示:
#include <windows.h>
#ifdef _DefineMyStruct//防止类型重复包含
class my
{
int i;
};

#endif

#ifdef _DefineMyVariable//防止变量定义声明重复包含
BOOL (WINAPI* closehandle)(HANDLE hObject)=CloseHandle;
int i=0;

#endif


       现在来分析:由于 变量类型只能包含一次,因此只能有一处#define _DefineMyVariable,其他地方必须为#undef _DefineMyVariable
我们可以把这个机会给2.cpp(当然其他cpp也可以),2.cpp还用了类类型,同时包含了变量i的声明还不算完,需要用extern将其引入。所以代码如下:
#define _DefineMyStruct
#define _DefineMyVariable
#include "1.h"

extern int i;
extern decltype(closehandle) closehandle;

void func()
{
i=1;
my me;
}

3.cpp由于只用到了变量,且变量已经“包含”给2.cpp,因此已经通用,用extern引入即可,代码如下:
extern int i;

void func1()
{
i=1;
}

1.cpp由于只用到类类型,需要include头文件了,代码如下:
#include <windows.h>

#define _DefineMyStruct
#undef _DefineMyVariable
#include "1.h"
void main()
{
my me;
}


再次优化:特意将extern声明放到1.h中,cpp只需要包含头文件即可,免去写extern了,代码如下:
1.h代码如下:
#include <windows.h>

class my
{
int j;
};

#ifdef _DefineMyVariable//防止变量定义声明重复包含
#undef _DefineMyVariable

decltype(MessageBox) *MyMessageBox=MessageBox;
int i=0;

#endif
extern int i;
extern decltype(MessageBox) *MyMessageBox;

1.cpp代码如下:
#include <windows.h>

#define _DefineMyVariable
#include "2.h"

void main()
{
my me;
MyMessageBox=NULL;
MyMessageBox=MessageBox;
MyMessageBox(NULL,"lichao890427","lichao890427",MB_OK);
}

2.cpp代码如下:
#include "2.h"

void func()
{
i=1;
my me;
MyMessageBox=NULL;
}

3.cpp代码如下:
#include "2.h"

void func1()
{
i=1;
MyMessageBox=NULL;
}

这已经是最简形式了!!!!!!!!!!!

现在我有更正规的方式,而且不用修改各个头文件,那就是:h文件只保留类型和函数声明,所有变量定义及全局变量及函数实现放到同名cpp文件中,然后把cpp文件加入工程,h文件还是原样包含在其他头文件里即可!
不过这样做遇到模板会出问题,众所周知,c++对于模板处理不好,带模板的类需要把声明和实现都写在h文件里才可认出,因此,事实上java里用模板更普遍,java对模板处理的比较好,泛型用的很“泛滥”
回复

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-5-4 11:33 , Processed in 0.033852 second(s), 30 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表