让我们看看_T的定义吧:
#define wxCONCAT_HELPER(text, line) text ## line /* could already be defined by tchar.h (it's quasi standard) */ #ifndef _T
#if !wxUSE_UNICODE #define _T(x) x #else /* Unicode */
/* use wxCONCAT_HELPER so that x could be expanded if it's a macro */ #define _T(x) wxCONCAT_HELPER(L, x) #endif /* ASCII/Unicode */ #endif /* !defined(_T) */
_T在UNICODE下面最终会被替换成L ## x。 ##是一个编译预处理指令,意味着让L和x贴在一起,比如L ## \最终就是L\,因此它可以把\转换成UNICODE字符串。那为什么上面的程序不行呢?让我们看看_T(\会被替换成什么:L ## pStr -> LpStr,哦,LpStr是一个新的标识符,如果你没有定义过它,你当然不能通过编译啦。 因此我们可以了解到_T这样的宏只能处理直接的常量字符串,不能处理其它的情况。而我们上面演示的那种情况需要我们动态的去转换编码,Windows有API可以帮助我们做到,C库也有函数可以帮助我们。恰好我曾经写过这样的代码,欢迎大家参考:ASCII/UNICODE/UTF8字符串互相转换的C++代码
对于_T宏,再说一点东西,或许你会感到奇怪为什么_T不直接定义成#define _T(x) L ## x,而要绕个圈子去调用wxCONCAT_HELPER呢?这实际上涉及到宏展开顺序和截断的问题。在这里,我们需要说一个宏参数的概念,这很函数的参数是类似的,这里_T(x)的x就是宏参数,好,记住下面一句话:
如果你定义的宏中使用了#或者是##的话,宏参数将不会被展开,也就是说_T(x)如果直接定义成L##x那么在下面这种情况就会出错( PS: #是给参数加引号的意思):
_T(__FUNCTION__),__FUNCTION__是一个预定义的宏,它代表了当前函数的名字,这个展开会是什么呢?L__FUNCTION__。为什么间接调用wxCONCAT_HELPER就能得到正确的结果呢?因为当我们调用wxCONCAT_HELPER的时候,__FUNCTION__已经被_T展开成了函数名。
说多了说多了,如果你觉得复杂可以暂时跳过这些东西,我只是顺便说说。 重定义的编译错误和链接错误
让我们在项目里面再添加一个Test.h头文件,方法是右击解决方案中的项目,添加,新建项,C++头文件,名称输入test.h。然后我们在test.h中输入:
/*#pragma once*/ void print() { }
回到main.cpp中: #include
编译一下我们会得到重定义的编译错误: error C2084: 函数“void print(void)”已有主体
或许你会说,你引用(#include)了两次,我没你那么傻,我只引用一次不就好了么?是的。你聪明,但是是小聪明哈,因为你不能保证每个人都不去引用它。
这个问题演示的是#pragma once的用处,让我们解开它的注释。编译成功!#pragma once的作用就在于防止头文件被多次引用。你或许见过 #ifndef __TEST_H__ #define__TEST_H__ 代码 #endif
这样的代码,它们的作用是一样的,如果你跟我一样懒,那么就用#pragma once,如果你打算去没有这个指令的编译器上编译代码,那么还是用后面一种方式吧。
现在让我们来见识一个对初学者稍微复杂一点的链接错误,用创建main.cpp的方法再添加一个test.h头文件,输入#include \即可。
让我们再编译一次。
1>test.obj : error LNK2005: \已经在 Main.obj 中定义
1>e:\\documents\\visual studio 2010\\Projects\\HelloWorld\\Debug\\HelloWorld.exe : fatal error LNK1169: 找到一个或多个多重定义的符号
如果说编译错误好找的话,链接错误对于初学者来说就有点麻烦了,聪明的初学者会去Google、百度寻找答案,笨的初学者就会找所谓的高手、前辈问,而这些高手Or前辈未必有心情为你解释。要解决这个错误有无数种方法。 1.内联,把print声明为内联函数。 inline void print() { }
这个方法的好处是简单,坏处是局限性太强,意味着你总是需要公开print的实现,因为内联函数必须在编译时就知道实现才行。 2.static,把print声明为static函数: static void print()。
这便告诉编译器,哥是唯一的,而且哥只能被本编译单元的代码调用,这和extern是对应的。简单来说,想要哥帮你做事,请先include哥声明的头文件,也就是#include \。 3..h头文件中只放声明,实现放到.cpp中去。
现在test.h中只有void print();,而实现在test.cpp中:
#include \ void print() {
int a = 1;
cout<< a++ << endl; }
这个时候有意思的是我们在main.cpp无需包含test.h头文件也可以引用print函数,因为print并非static的函数: void print(); int main() { print(); print(); return 0; }
但是声明一下是必须的。
由于百度空间的帖子的篇幅是有限制的,因此今天只好就说这么几点了。新的内容请大家等候下一章。
Visual C++ 2010新功能之static_assert(静态断言)
写过模板的同学对下面的这样的代码应该会很熟悉: template < typename T, unsigned MaxLen > class static_array {
private:
typedef DBSOFT_TR1::array< T, MaxLen > array_type;
typedef char __TEMPLATE__CHECK__DUMMY__[sizeof(T)]; // 如果sizeof無法計算T的大小會報錯
typedef char __TEMPLATE__CHECK__LEN_DUMMY__[MaxLen-1]; // 不能用負數、0等來具現化
在编译时它会在我们使用void或者长度小于等于0去具现化这个模板类的时候提示编译错误。以上面的代码为例,如果我们这样去使用这个static_array的话: #include