宏定义中的#,##和C++中的虚函数表问题
前几天,有个同学问我宏定义中的#和##都是什么意思啊,说实话,我也不知道,不知道怎么办,google啊!说实话,自己过去对宏定义还真没有深入了解,只会使用最简单的宏定义,自从学了C++之后,基本上都不用宏定义了,用const来直接取代了,《C++ Primer》里也强烈推荐用const来取代宏定义。
关于宏定义的具体深入介绍,我也不想多说什么,想看的,请前往这个网址,个人感觉讲得挺好: http://learn.akae.cn/media/ch21s02.html 。不过在这里就事论事,只简单地讨论下#和##。
一句话:# —— 字符串,##——连接两个参数。如果这样写你还不明白,那么就看下面这个例子吧:
1: #include <iostream>
2: using namespace std;
3:
4: #define TEST(pid) (cout<<para##pid<<endl);
5: #define TEST2(p) (cout<<#p<<endl);
6: int main()
7: {
8: int para3 = 3;
9: int para2 = 2;
10: TEST(2); //<==>cout<<para2<<endl;
11: TEST(3); //<==>cout<<para3<<endl;
12:
13: TEST2(test) //<==>cout<<"test"<<endl;
14: TEST2("test2"); //<==>cout<<""test2""<<endl;
15: return 0;
16: }
运行的结果如下图所示:
现在,应该明白了吧,感觉很神奇!!!
--------------------------------------------------------------------------------------------------------------华丽的分割线-----------------------------------------------------------------------------------------------------------------------
接下来再说说C++中的虚函数表吧
前一段时间,还有一学弟问我一虚函数表的问题,当初学的时候,大体看过,不过这么长时间没用C++了,快要忘记了。这里说一下。当时他问我的问题的大体代码如下:
1: #include <iostream>
2: using namespace std;
3:
4: class Base {
5: public:
6: virtual void f() { cout << "Base::f" << endl; }
7: virtual void g() { cout << "Base::g" << endl; }
8: virtual void h() { cout << "Base::h" << endl; }
9: };
10:
11: int main()
12: {
13: typedef void(*Fun)(void); Base b;
14: Fun pFun = NULL;
15: cout << "虚函数表地址:" << (int*)(&b) << endl;
16: cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
17: // Invoke the first virtual function
18: pFun = (Fun)*((int*)*(int*)(&b));
19: pFun();
20: }
运行结果如下:
他不明白的是为什么第一个cout和第二个cout的结果不一样。其实说白了,按我自己的理解,如果你的类中的虚函数,那么在编译的时候,编译器会自动地在类中添加一个成员变量,这个成员变量位于类中所有成员的前面,所以指向这个类的对象的指针同时也是指向这个成员变量的指针,所以第一个cout的结果也就很自然而然了。同时,这个成员变量的内容指向内存中存放这个类所属的虚函数表的地址。所以我们对第二个cout进行一下解析。首先看(int)(&b),很简单,就是取b的地址,同时将其地址由Base*类型强制转换成int类型的指针,再加一个*,就是取这个指针所指向的内存的内容,也就是这个类的虚函数表的第一个函数的地址。再接下来我也不是能很清楚地解释出来,按我自己的理解,就是前面得到了一个地址,如果直接输出,就是一个十进制数,再加上(int *)之后,就是将这个十进制数强制转换成指针类型,而我常用十六进制表示内存位置,所以输出的就是原地址的十六进制表示。以上只是我个人的理解,如有不正确,请指出。
同时,通过这个实例我也发现,将一个十进制数转换成16进制数,最简单的办法就是将其强制转换成(int *),然后再输出,比如“cout<<(int *)10<<endl;”,输出的结果是0xa。