发现重复定义是由于从两个不同的路径包含了同一个头文件而引起的,同事也建议从另外一个路径打开工程试试, 这才慢慢发现了原因。这个原因可能有些拗口,而事实上要出现这种错误也有些"曲折", 让我从不同情况下的类型重定义来解释一下吧。
我总结的类型重定义情况有三。
一、没有在文件头加#pragma once指示符。
Type1.h:
//#pragma once
class Type
{
};
Main.cpp:
#include "Type1.h"
#include "Type1.h"
int main(int argc, char *argv[])
{
return 1;
}
#pragma once的作用是保证本文件只被编译一次,如果没有在Type1.h中加这句话,那么在main.cpp里面包含了两次Type1.h, 就相当于在main.cpp里面定义了两次Type类, 自然就是类型重定义了。
二、两个不同的头文件中定义了相同的类型(均有#pragma once)
Type1.h:
#pragma once
class Type
{ };
Type2.h:
#pragma once
class Type
{ };
Main.cpp:
#include "Type1.h"
#include "Type2.h"
int main(int argc, char *argv[])
{
return 1;
}
这里main.cpp中同时包含了Type1.h, Type2.h两个头文件, 虽然其文件头都有#pragma once,但因为是不同的文件, 预编译器还是会两次把Type类的定义放在Main.cpp中, 所以也会出现了重定义。
三、从两个不同的路径包含了同一个头文件
前面两种是比较常见, 也是比较容易解决的情况, 而这里要讲的第三种情况, 比较少见, 而且一般出现在有虚拟映射盘的时候。(这样才能做到从两个不同的路径包含同一个头文件), 其他会在什么时候出现, 我还没想到, 知道的朋友顶一下:)。下面我来分析一下:
1) 有VC工程在D:\Test目录下。
2) 映射虚拟盘X为D:\Test.
不熟悉的网友可以按此操作: 开始->运行->在运行窗口输入:cmd->在cmd窗口输入:
Subst X: D:\Test
3) 该工程有文件Type1.h, main.cpp
Type1.h:
#pragma once
class Type
{ };
Main.cpp:
#include "Type1.h"
#include "X:\Type1.h"
int main(int argc, char *argv[])
{
return 1;
}
这里我们在main.cpp这样包含了两个头文件, 从本质上来讲, 它们都对应于物理盘D:\Test下的文件Type1.h, 是同一个文件。但在不同的操作下, VC对其有不同的解释。#include "X:\Type1.h"用的是绝对路径, 自然没有什么异议, 但#include "Type1.h"却有些变化:
- 假如我从D:\Test\下打开工程, 那么#include "Type1.h"其实就是#include "D:\Test\Type1.h"
- 假如从X:\下打开工程,那么#include "Type1.h"就解释为#include "X:\Type1.h"
4) 在D:\Test下打开工程, 编译, 出现类型Type重复定义错误
这种情况下,main.cpp预编译为:
Main.cpp:
#include "D:\Test\Type1.h"
#include "X:\Type1.h"
int main(int argc, char *argv[])
{
return 1;
}
#pragma once只保证本文件被编译一次, 这里VC将其认为是两个不同的文件, 所以都要编译, 出现编译错误自然也就不奇怪了。
当然, 这里如果从X:\ 下打开工程的话,VC就会认为都是从X:\Type1.h下包含这个文件,#pragma once起到了作用, 也就不会出现类型重定义了
四、总结
我在VC7, VC8,和Dev C++中都测试了第三种情况, 发现只有Dev C++是可以通过编译的。这可能是微软VC的#pragma once还不够智能吧,轻易的被Windows的虚拟盘给蒙蔽了双眼, 看不到其本质(只是猜测, 或许VC这么处理是有其他用意的)。
因为在稍大一点的工程开发中, 我们一般都会用虚拟盘来方便工作, 一是访问快捷,简化了路径, 二是因为多人协同开发,我们一般希望大家源代码路径相同,但我们不应强制要求大家都把源代码放死在某一目录下, 这时把你放源代码的路径映射为一个虚拟盘(比如说统一为X:)就能把大家的代码路径统一起来了。但是另一方面,有了虚拟盘, 就为出现类型重定义提供了条件, 以下是我得出的两个解决方法:
1) 抛弃#pragma once使用古老但集稳定性与移植性于一身的
#ifndef _XXX_H
#define _XXX_H
//...
#endif
来保证头文件只被编译一次。这样不管是包含两个相同的文件,还是包含两个不同的文件,或是包含两个文件相同但路径不同的文件, 只要_XXX_H被定义过, 就不会再编译那个编译(但这里我们要保证_XXX_H的唯一性, 如果两个不同的头文件里用了同一_XXX_H,是会出问题的)
2) 在包含头文件时,不要使用绝对路径, 哪怕那是虚拟盘的绝对路径。
C++中的类型重定义的更多相关文章
-
多.h项目出现的问题:使用了预编译头依然出现error LNK2005:***obj已在***obj中定义与c++ error C2011: “xxx”:“class”类型重定义解决办法
使用了预编译头依然出现error LNK2005:***obj已在***obj中定义 造成该问题的可能性比较多,本人将在今后遇到时添加进来,今天先放出本人遇到的一种情况. 多重包含含有变量定义的.h文 ...
-
sockaddr struct 类型重定义
windows.h和winsock2.h有类型重定义我是知道的,本来就一个库来说没问题,把winsock2放到windows.h前或先定义WIN32_LEAN_AND_MEAN都能解决问题但现的出了问 ...
-
C# - 为值类型重定义相等性
为什么要为值类型重定义相等性 原因主要有以下几点: 值类型默认无法使用 == 操作符,除非对它进行重写 再就是性能原因,因为值类型默认的相等性比较会使用装箱和反射,所以性能很差 根据业务需求,其实际相 ...
-
error C2011: “timespec”:“struct”类型重定义
error C2011: “timespec”:“struct”类型重定义 C++ pthread pthread.h 中的 timespec 和time.h 中的 结构定义重复了 ,同时两个头文件中 ...
-
“sockaddr”: “struct”类型重定义的错误的解决办法《转》
原帖地址:https://blog.csdn.net/clever101/article/details/100163301 windows.h和winsock2.h存在有类型重定义,往往体现在VC程 ...
-
c#中枚举类型的定义与使用
介绍枚举是一个指定的常数,其基础类型可以是除 Char 外的任何整型.如果没有显式声明基础类型,则使用 Int32.编程语言通常提供语法来声明由一组已命名的常数和它们的值组成的枚举.定义默认基数从O开 ...
-
error C2011: “Picture”:“struct”类型重定义
今天引用外来库时出现问题,也许是版本问题. 错误如下: .....\oursun\cincludes\quickdraw.h(309): error C2011: “Picture”:“struct” ...
-
C++中重载、重定义、重写概念辨析
重载:函数名相同,函数的参数个数.参数类型或参数顺序三者中必须至少有一种不同.函数返回值的类型可以相同,也可以不相同.发生在一个类内部. 重定义:也叫做隐藏.覆盖,子类重新定义父类中有相同名称的非虚函 ...
-
struct 类型重定义
类型定义的那个头文件只需要在功能源文件里#include 开始在主函数源文件里也#include,所以出现了重定义
随机推荐
-
awk打开多个文件的方法
1.当awk读取的文件只有两个的时候,比较常用的有三种方法(1)awk 'NR==FNR{...}NR>FNR{...}' file1 file2 (2)awk 'NR==FNR{...}NR! ...
-
centos svn服务器安装
1.安装必须的软件 yum install httpd httpd-devel subversion mod_dav_svn mod_auth_mysql 2.创建代码库 mkdir -p /root ...
-
Python OpenCV ——Matplotlib显示图片
Color image loaded by OpenCV is in BGR mode.But Matplotlib displays in RGB mode.So color images will ...
-
Failed to load session “ubuntu” -- 12.04
在使用ubuntu 12.04的时候,之前不喜欢Unity桌面,就按照网上提示的安装了Gnome桌面,并且成功了! 可是,最近又想把Unity装回来,心想,这不就是安装一软件的活嘛,很简单!于是就卸载 ...
-
转载Jquery中的三种$()
1.$()可以是$(expresion),即css选择器.Xpath或html元素,也就是通过上述表达式来匹配目标元素. 比如:$("a")构造的这个对象,是用CSS选择器构建了一 ...
-
Android -- RecyclerView实现顶部吸附效果
1,先来看一下今天实现的效果: 2,这次的效果是使用ItemDecoration来实践的,来看一看我们的实现吧 第一步:首先添加依赖,由于我们这些数据是请求网络的,所以一下我们添加网络框架依赖.Rec ...
-
Linux记录-Linux 企业运维人员最常用 150 个命令
命令 功能说明 线上查询及帮助命令 (2 个) man 查看命令帮助,命令的词典,更复杂的还有 info,但不常用. help 查看 Linux 内置命令的帮助,比如 cd 命令. 文件和目录操作命令 ...
-
php json中文被转义
php 5.4 json_encode($str, JSON_UNESCAPED_UNICODE); 5.4版本以下 方法一function encode_json($str){ $code = js ...
-
yum源安装php报错缺少libmcrypt.so.4()(64bit)库
https://blog.csdn.net/programercch/article/details/56282184
-
201709025工作日记--更新UI方法
1.handler+Thread 和 runOnUIThread 和 handler.post 方法 区别: 从实现原理上,两者别无二致,runOnUiThread也是借助Handler实现的. 对 ...