当前位置:  编程技术>c/c++/嵌入式

基于C++类型重定义的使用详解

    来源: 互联网  发布时间:2014-10-14

    本文导语:  这几天工作时碰到一个C++的编译错误(我使用的是Visual C++ 7.0),说是有一个类重复定义,仔细想想我们的这个项目也是做了好几个Release了, 内部代码应该不会有这样的低级错误, 真把类型给重复定义了,检查结果正如我预料...

这几天工作时碰到一个C++的编译错误(我使用的是Visual C++ 7.0),说是有一个类重复定义,仔细想想我们的这个项目也是做了好几个Release了, 内部代码应该不会有这样的低级错误, 真把类型给重复定义了,检查结果正如我预料的一样。 就这样, 我左右没找到原因,被一个编译错误给卡在那里了。(在我的概念中, 程序错误的等级为:编译错误->链接错误->逻辑错误, 此错误属于最低级 )。这时我仔细看了一下错误提示, 发现重复定义是由于从两个不同的路径包含了同一个头文件而引起的,同事也建议从另外一个路径打开工程试试, 这才慢慢发现了原因。这个原因可能有些拗口,而事实上要出现这种错误也有些"曲折", 让我从不同情况下的类型重定义来解释一下吧。
我总结的类型重定义情况有三。
一、没有在文件头加#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:TestType1.h"
•假如从X:下打开工程,那么#include "Type1.h"就解释为#include "X:Type1.h"
4) 在D:Test下打开工程, 编译, 出现类型Type重复定义错误
这种情况下,main.cpp预编译为:
代码如下:

Main.cpp:
#include "D:TestType1.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++四种强制类型转换方式介绍
  • 为什么linux下的C++程序这么少见? 请问那里有linux下的C++程序?什么类型的程序都可以.
  • 深入理解:Java是类型安全的语言,而C++是非类型安全的语言
  • 请问c++中的DWORD是什么类型??
  • 深入C++四种强制类型转换的总结
  • C++如何通过ostringstream实现任意类型转string
  • 关于C++数据类型转换的问题
  • 利用C++实现从std::string类型到bool型的转换
  • 请问,LINUX的C++中,如何将string类型与char数组一次性互相转换?
  • 用C++实现strcpy(),返回一个char*类型的深入分析
  • 浅谈C++中的string 类型占几个字节
  • 从汇编看c++中变量类型的深入分析
  • c++动态内存空间示例(自定义空间类型大小和空间长度)
  • c++中的4种类型转化方式详细解析
  • 基于C++浮点数(float、double)类型数据比较与转换的详解
  • 解析C++中四种强制类型转换的区别详解
  • C++中Operator类型强制转换成员函数解析
  • C++类型转换归纳总结
  • c++显式类型转换示例详解
  • c++隐式类型转换示例分享
  • 用标准c++实现string与各种类型之间的转换
  • 解决无法在unicode和非unicode字符串数据类型之间转换的方法详解
  • 深入C#中使用SqlDbType.Xml类型参数的使用详解
  • 深入Java不可变类型的详解
  • 深入Unix时间戳与C# DateTime时间类型互换的详解
  • c#入门之类型转换详解
  • C#用户定义类型转换详解
  • PHP的变量类型和作用域详解
  • jquery判断浏览器类型实例详解
  • MySQL的数据类型和建库策略分析详解
  • Python内置数据类型详解
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • java命名空间javax.xml.ws注释类型webserviceref成员方法: type定义参考
  • 请问:在类中能不能定义static pthread_mutex_t 类型的成员变量啊,怎么老是说没有这种类型啊?
  • java命名空间javax.annotation注释类型resource的类成员方法: authenticationtype定义及介绍
  • 如何搜索系统定义类型的原始类型
  • java命名空间javax.annotation注释类型resource的类成员方法: type定义及介绍
  • 请问一个方法的返回值类型为一个复数,那么怎样定义这个方法的返回值类型
  • java命名空间javax.xml.ws注释类型webserviceref成员方法: value定义参考
  • 枚举类型变量如何定义与初始化,如何给该变量加入新值?
  • java命名空间javax.annotation注释类型resource的类成员方法: shareable定义及介绍
  • 菜鸟问题:如何了解系统关于某个C语言变量类型是怎样定义的?
  • java命名空间javax.jws注释类型webparam的类成员方法: mode定义及介绍
  • 请问LINUX 下 _int64类型是怎么定义的啊
  • java命名空间javax.xml.ws注释类型webserviceref成员方法: wsdllocation定义参考
  • LINUX 下自定义类型的问题
  • java命名空间javax.xml.bind.annotation注释类型xmlelementref的类成员方法: type定义及介绍
  • python 判断自定义对象类型
  • java命名空间javax.xml.bind.annotation注释类型xmlenum的类成员方法: value定义及介绍
  • 请问linux下如何方便的找到数据类型的定义?
  • java命名空间javax.xml.ws注释类型responsewrapper成员方法: targetnamespace定义参考
  • 为什么我定义的类 总被编译程序提示要求 改为抽象类型?
  • java命名空间javax.xml.ws注释类型responsewrapper成员方法: classname定义参考
  • 关于定义void类型的变量的问题,高手请进
  • java命名空间javax.management注释类型descriptorkey的类成员方法: value定义及介绍
  • java 基本类型或可序列化类型,这里说的可序列化类型是什么意思???具体都有哪些类型??谢谢
  • java命名空间javax.xml.ws注释类型webfault成员方法: name定义参考
  • 求救:关于将类型名称字符串转变成类型相应类型的问题
  • java命名空间javax.xml.ws注释类型requestwrapper成员方法: localname定义参考
  • 请问能否把任何类型数据转换为一种类型,然后从这种类型又转换回去?例如:int>>Object Object>>int
  • java命名空间javax.xml.ws注释类型responsewrapper成员方法: localname定义参考
  • c# 所有类型都从Object类型派生
  • java命名空间java.lang.annotation注释类型retention的类成员方法: value定义及介绍


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3