Visual Studio系列中产品中,Visual Studio 6.0是最经典的一个版本,虽然后来有Visual Studio .NET 2003,以及2005,也确实添加了很多让我觉得激动的特性,但是从使用细节的细腻程度上来看,VS 6.0无疑是最棒的。我们一些同事甚至试图把2005的C++编译器独立的拿到Visual Studio 6.0中来用,也不愿意升级到.NET上来用,可见其魅力。
和VS 6.0这个产品的成熟相比,VC++ 6.0的编译器的的确确相对来说有些糟糕,其中最被诟病的是对模板技术支持很不好。下面我想做的一件事情,就是向那些继续留恋VC++ 6.0的朋友,介绍一些小花招,来避开VC++ 6.0的一些编译器缺陷。
1)for (type var=expression;;) 中变量var的作用域问题。
按照C++标准,这里定义的变量var出了for循环应该被销毁。也就是说下面这段代码是有效的:
for (int i = 0; i < 100; ++i)
func();
for (int i = 0; i < 100; ++i)
func2();
而下面这段代码应该编译不过:
for (int i = 0; i < 100; ++i)
{
if (has_found_it())
{
handle_find_result();
break;
}
}
if (i == 100)
do_not_found();
然而VC++ 6.0对于第一段代码会报变量i重复定义错误,而第二段代码编译通过。
为了让VC++ 6.0的for语句看起来符合C++标准,你可以这样做:
#define for if (0); else for
你会发现很有趣,这样define一下后,VC++ 6.0的for语句完全符合C++标准了!而且由于编译器的优化,Release版本不会增加任何额外的开销。
喜欢“钻牛角尖”的朋友可能会说:嗯,不错的主意。但是——为什么不这样做:
#define for if (1) for
嗯?看起来也可以。还是让我们看一个用例:
if (cond)
for (int i = 0; i < 100; ++i)
func1();
else
func2();
进行宏代码展开后,成为:
if (cond)
if (1)
for (int i = 0; i < 100; ++i)
func1();
else
func2();
这个结果显然不能符合我们的原意。这里func2();语句永远得不到执行机会。
2)模板参数类型如果不出现在参数列表中,则不能作为返回值类型。
由于编译器的缺陷,VC++ 6.0不支持以下这种用法:
template <class T1, class T2>
T1 func(T2 arg)
{
T1 var;
... // 处理var过程
return var;
}
void test()
{
int result1 = func<int>(1);
double result2 = func<double>(2);
};
很抱歉,这种用法VC++ 6.0不支持。让人恼火的是,VC++ 6.0编译时不会提示错误,但是生成的执行代码却很成问题。
究其原因,是因为VC++ 6.0的template技术是在编译器的较高层次做的,真正的编译器核心并不考虑模板。以上面的代码为例,对编译器核心来说,只是有两个重载函数而已:
int func(int arg);
double func(int arg);
如果是普通情况,只是返回值不同的函数,是不能同时存在的,编译器应该认为这是一个错误。但是很在模板情况下,这两个函数被简单认为是同一个函数。因为VC++ 6.0会为每个函数根据它的:
1)所在的namespace;
2)所在的类的类名(如果是成员函数);
3)函数名;
4)函数调用方式(cdecl、stdcall还是fastcall);
5)所有参数的类型;
而生成一个唯一标识该函数的函数名。这个过程叫Name Mangling,是所有C++编译器都要进行的工作。而另一个背景是,很多C++编译器生成的目标文件(.obj文件)有一些和模板相关的特殊信息,包括也标识了某个函数是否模板函数。这是因为一个模板函数在多个源文件(.cpp文件)中被调用的话,这个模板函数就会在这些源文件编译生成的目标文件(.obj文件)中都定义(definition)一份。为了支持模板,link程序显然必须知道这个函数是模板函数,从而随意选择一个定义(丢弃其余的定义),而不是报符号重复定义错误。
因为函数名、参数列表等完全一致,所以这两个函数Name Mangling后生成的名字是一样的,并且,它们都被标识为这是模板函数。从而,link程序在工作的时候,简单地将其中一个函数定义给抛弃了。
那么,如果我们非要提供上述的func函数,怎么办?我们来点花招:
template <class T1>
class func
{
private:
T1 var;
public:
template <class T2>
func(T2 arg)
{
... // 处理var过程
}
operator T1() const
{
return var;
}
};
我们再来使用func这个“函数”:
void test()
{
int result1 = func<int>(1);
double result2 = func<double>(2);
};
呵呵,你会发现,它还真象是你期望的正常工作。
3)仿真VC++提供的关键字__uuidof。
这个技巧不是针对VC++ 6.0缺陷的,而是针对VC++扩展语法的。这个技巧的来由,是为了某些希望有一天有可能要脱离Visual C++环境进行开发的人员。为了脱离VC++,你需要谨慎使用它的所有扩展语法。例如本文讨论的__uuidof。我们先来看看一个例子:
class __declspec(uuid("B372C9F6-1959-4650-960D-73F20CD479BA")) Class;
struct __declspec(uuid("B372C9F6-1959-4650-960D-73F20CD479BB")) Interface;
void test()
{
CLSID clsid = __uuidof(Class);
IID iid = __uuidof(Interface);
...
}
这比起你以前定义uuid的方法简单多了吧?可惜,这样好用的东西,它只在VC++中提供。不过没有关系,我们这里介绍一个技巧,可以让你在几乎所有C++编译器中都可以这样方便的使用__uuidof。这里没有说是所有,是因为我们使用了模板特化技术,可能存在一些比较“古老”的C++编译器,不支持该特性。
也许你已经迫不及待了。好,让我们来看看:
#include <string>
#include <cassert>
inline
STDMETHODIMP_(GUID) GUIDFromString(LPOLESTR lpsz)
{
HRESULT hr;
GUID guid;
if (lpsz[0] == '{')
{
hr = CLSIDFromString(lpsz, &guid);
}
else
{
std::basic_string<OLECHAR> strGuid;
strGuid.append(1, '{');
strGuid.append(lpsz);
strGuid.append(1, '}');
hr = CLSIDFromString((LPOLESTR)strGuid.c_str(), &guid);
}
assert(hr == S_OK);
return guid;
}
template <class Class>
struct _UuidTraits {
};
#define _DEFINE_UUID(Class, uuid)                                        \
template <>                                                              \
struct _UuidTraits<Class> {                                              \
static const GUID& Guid() {                                          \
static GUID guid = GUIDFromString(L ## uuid);                    \
return guid;                                                     \
}                                                                    \
}
#define __uuidof(Class)    _UuidTraits<Class>::Guid()
#define DEFINE_CLSID(Class, guid)                                        \
class Class;                                                         \
_DEFINE_UUID(Class, guid)
#define DEFINE_IID(Interface, iid)                                       \
struct Interface;                                                    \
_DEFINE_UUID(Interface, iid)
这样一来,就已经模拟出一个__uuidof关键字。我们可以很方便进行uuid的定义。举例如下:
DEFINE_CLSID(Class, "{B372C9F6-1959-4650-960D-73F20CD479BA}");
DEFINE_IID(Interface, "{B372C9F6-1959-4650-960D-73F20CD479BB}");
void test()
{
CLSID clsid = __uuidof(Class);
IID iid = __uuidof(Interface);
...
}
在VC++中,为了与其他编译器以相同的方式来进行uuid的定义,我们不直接使用__declspec(uuid),而是也定义DEFINE_CLSID, DEFINE_IID宏:
#define DEFINE_CLSID(Class, clsid)           \
class __declspec(uuid(clsid)) Class
#define DEFINE_IID(Interface, iid)           \
struct __declspec(uuid(iid)) Interface
这样一来,我们已经在所有包含VC++在内的支持模板特化技术的编译器中,提供了__uuidof关键字。通过它可以进一步简化你在C++语言中实现COM组件的代价。
附注:关于本文使用的C++模板的特化技术,详细请参阅C++文法方面的书籍,例如《C++ Primer》。其实这个技巧在C++标准库——STL中有一个专门的名字:traits(萃取),你可以在很多介绍STL的书籍中见到相关的介绍。

VC++ 6.0的小花招相关推荐

  1. vc c语言小游戏源代码,用VC++6.0实现石头剪刀布游戏的程序

    源程序是从网上看到的, geek_monkey于2015年3月3日修改了bug(输入字符非石头剪刀布都算是玩家赢) 编译环境为VC++6.0 增加"上帝模式"和数据统计,纯属娱乐. ...

  2. c c mySQL机票设计_期末课程设计之 c++操作mysql完成机票预订系统(vc 6.0配置mysql环境)...

    本次大二期末的课程设计题是 完成一个机票预订系统,主要方法是通过c++函数操作mysql数据库来实现系统的功能,程序中还是有些许不足,在此传上请多多指教(毕竟小菜鸟). 程序复制到vc 6.0(原因是 ...

  3. c语言中文网 vc++6.0下载量_【新手必看】C语言开发环境,请查收!

    C语言开发环境的选择, 貌似是告诉我们,工欲善其事,必先利其器. 01.Visual Studio Visual Studio(简称 VS),它是 Windows 下的标准 IDE,VS系列为IDE( ...

  4. 基于VC++6.0的DLL开发

    原文地址:http://blog.163.com/twnhr@126/blog/static/78927547200910254346804/ 基于VC++6.0的DLL开发 最近在开发一个基于网络的 ...

  5. 分享几个Python小技巧函数里的4个小花招

    前面讲了很多内容都是关于python的变量,数据结构,下面我们来谈一谈python的函数.python里的函数知识点大概分为 基础的定义使用 , 作用域 和 参数传递 , 高级用法 ,其中参数传递最为 ...

  6. c语言中tc和vc有什么区别,转:写C++用什么编译器好——TC,VC++6.0,DEV C++,VS2005浅显比较...

    TC: 现在估计给你钱,你也不会要用TC了吧.一个复制粘帖都不觉得顺手的编译器,确实是不方便的. VC 6.0: 虽然这个软件比较小,一般就是几百兆,用起来也差强人意,不过难免还是存在一些小问题: 小 ...

  7. c语言用字符画一个椭圆,用vc++6.0 程式设计画一个椭圆出来,求完整原始码

    用vc++6.0 程式设计画一个椭圆出来,求完整原始码以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 用vc++6.0 ...

  8. VC++6.0在Win11下无法运行

    最近小编将电脑重置了,发现VC++6.0安装后无法运行,经过小编的努力,发现了问题所在,下面跟随小编来看看吧! 首先,打开你的Visual C++ 6.0安装目录,这里小编的是目录是D:\BaiduN ...

  9. 使用VC++6.0创建MFC对话框程序

    使用VC++6.0创建MFC对话框程序

最新文章

  1. python的数据清理_Python数据清理,清洗
  2. 深入理解 SVG 系列(一) —— SVG 基础
  3. opencv的K近邻算法
  4. Thread线程的深刻理解和代理方法参数[有图有真相]
  5. Android studio 3.x 安装genymotion插件
  6. 计算机教师自媒体方向,教师和自媒体,我该选择哪个深耕?
  7. C语言实现的图的深度搜索与广度搜索程序
  8. 第五篇:数据预处理(二) - 异常值处理
  9. TikTok独立站该怎么布局?
  10. 日本定了一个小目标,在2030年让五分之一的汽车实现自动驾驶
  11. shell脚本 把一个文件的内容全部转换为大写
  12. phpmyadmin 安装mysql5.6
  13. JSP教程:学习路线和开发工具安装视频
  14. 迅雷的FLV文件迷你播放器
  15. Java实现:房贷计算器 (关键词:公积金、首付、等额本息、等额本金)
  16. Java判断字符串是否为数字(正负、小数)
  17. 阿里云备案流程和操作步骤详解(图文教程)
  18. 爬取网易云音乐排行榜
  19. Python爬虫数据存储之TXT文本
  20. 《程序员》杂志社 2010SD软件开发2.0大会随笔

热门文章

  1. 什么是POM maven
  2. 利用脚本生成GUID
  3. WebSocket 协议
  4. Linux下设置进程使用指定核的CPU
  5. Java 读取 dwg 转换 dxf
  6. Razor:从aspx到cshtml常见错误及正确书写方法
  7. Java 算法-异或校验和
  8. cocos2dx小游戏数据签名算法破解
  9. 用计算机的英语造句process,process的用法总结大全
  10. java initcause_Java 异常