::是C++里的“作用域分解运算符”。比如声明了一个类A,类A里声明了一个成员函数voidf(),但没有在类的声明里给出f的定义,那么在类外定义f时,就要写成voidA::f(),表示这个f()函数是类A的成员函数。例如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class
CA {
public
:
int
ca_var;
int
add(
int
a,
int
b);
int
add(
int
a);
};
//那么在实现这个函数时,必须这样书写:
int
CA::add(
int
a,
int
b)
{
return
a + b;
}
//另外,双冒号也常常用于在类变量内部作为当前类实例的元素进行表示,比如:
int
CA::add(
int
a)
{
return
a + ::ca_var;
}
//表示当前类实例中的变量ca_var。
|
C中的宽字符基于wchar_t数据型态,它在几个表头文件包括WCHAR.H中都有定义,像这样:
typedef unsigned short wchar_t ;
因此,wchar_t数据型态与无符号短整数型态相同,都是16位宽。
要定义包含一个宽字符的变量,可使用下面的语句:
wchar_t c = 'A' ;
变量c是一个双字节值0x0041,是Unicode表示的字母A。(然而,因为Intel微处理器从最小的字节开始储存多字节数值,该字节实际上是以0x41、0x00的顺序保存在内存中。如果检查Unicode文字的计算机储存应注意这一点。)
您还可定义指向宽字符串的指针:
wchar_t * p = L"Hello!" ;
注意紧接在第一个引号前面的大写字母L(代表「long」)。这将告诉编译器该字符串按宽字符保存-即每个字符占用2个字节。通常,指针变量p要占用4个字节,而字符串变量需要14个字节-每个字符需要2个字节,末尾的0还需要2个字节。
同样,您还可以用下面的语句定义宽字符数组:
static wchar_t a[] = L"Hello!" ;
该字符串也需要14个字节的储存空间,sizeof (a) 将返回14。索引数组a可得到单独的字符。a[1] 的值是宽字符「e」,或者0x0065。
虽然看上去更像一个印刷符号,但第一个引号前面的L非常重要,并且在两个符号之间必须没有空格。只有带有L,编译器才知道您需要将字符串存为每个字符2字节。稍后,当我们看到使用宽字符串而不是变量定义时,您还会遇到第一个引号前面的L。幸运的是,如果忘记了包含L,C编译器通常会给提出警告或错误信息。
您还可在单个字符文字前面使用L前缀,来表示它们应解释为宽字符。如下所示:
wchar_t c = L'A' ;
但通常这是不必要的,C编译器会对该字符进行扩充,使它成为宽字符。
#include <stdio.h>
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)
#define p(a) #@a
int main(int argc, char* argv[])
{
printf("%s\n", g(f(1,2)));
printf("%s\n", h(f(1,2)));
printf("%c, %d\n", p(1), p(1));
return 0;
}
printf("%s\n", g(f(1,2)));
printf("%s\n", h(f(1,2)));
printf("%c, %d\n", p(1), p(1));
有一道经典的C语言问题,关于宏定义中#和##符号的使用和宏定义展开问题
程序如下:
#include <stdio.h>
#define f(a,b) a##b
#define g(a) #a
#define h(a) g(a)
int main()
{
printf("%s\n", h(f(1,2)));
printf("%s\n", g(f(1,2)));
return 0;
}
答案:第一行:12 第二行:f(1,2)
说明:
1、关于符号#和##
两个符号都只能用于预处理宏扩展。不能在普通的源码中使用它们,只能在宏定义中使用。
简单的说,#是把宏参数变为一个字符串,##是把两个宏参数连接在一起。
关于这两个符号的具体意义和用法可以参见两篇文章:
#和##在宏替换中的作用 http://www.linuxidc.com/Linux/2014-06/102921.htm
C/C++ 宏中"#"和"##"的用法 http://www.linuxidc.com/Linux/2014-06/102924.htm
还有GCC帮助文档上的解释:
3.4 Stringification
3.5 Concatenation
2、关于宏展开
预处理过程的几个步骤:
1)字符集转换(如三联字符)
2)断行链接/
3)注释处理,/* comment */,被替换成空格
4)执行预处理命令,如#inlcude、#define、#pragma、#error等
5)转义字符替换
6)相邻字符串拼接
7)将预处理记号替换为词法记号
第4)步即如何展开宏函数的规则:在展开当前宏函数时,如果形参有#或##则不进行宏参数的展开,否则先展开宏参数,再展开当前宏。
宏替换顺序英文描述如下:
A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token, is replaced by the corresponding argument after all macros contained therein have been expanded.
3、总结
综合以上,对于这道题来说,第一行h(f(1,2)),由于h(a)非#或者##所以先展开其参数f(1,2),即12,所以变成h(12),然后再宏替换为g(12),再次替换为12。
第二行g(f(1,2)),宏g(a)带有#,所以里面的f(1,2)不展开,所以变成f(1,2)
类似的这种问题在《你必须知道的495个C语言问题》中出现过,在121页的“预处理功能”的问题11.19,有兴趣的朋友可以看一看。
C++ Primer Plus 第6版 中文版 清晰有书签PDF+源代码 http://www.linuxidc.com/Linux/2014-05/101227.htm
读C++ Primer 之构造函数陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm
读C++ Primer 之智能指针 http://www.linuxidc.com/Linux/2011-08/40177.htm
读C++ Primer 之句柄类 http://www.linuxidc.com/Linux/2011-08/40175.htm
C++11 获取系统时间库函数 time since epoch http://www.linuxidc.com/Linux/2014-03/97446.htm
C++11中正则表达式测试 http://www.linuxidc.com/Linux/2012-08/69086.htm