C++的优秀特性4:指针
(转载请注明原创于潘多拉盒子)
其实指针不是C++的特性,而是地地道道的C的特性。有人说C++继承了C的指针,实在是败笔,造成内存泄漏云云,纯粹是不懂。可以这么说,如果没有指针,C++会逊色很多,应用的场景也会大大缩小。
指针是一个变量,这个变量和一个int型的变量没有太大的不同,只是这个变量里存储的是它指向的对象的内存地址。
指针可以指向任何对象,包括内置类型(int、long、double、float等),对象类型,函数入口,甚至另外一个指针或者可以指向任何类型 。
涉及指针的操作符包括:定义指针(*)、解引用(*)、取地址(&)、取成员(->)、偏移解引用([]),偏移(+、-)、求偏移(-)。
先看看几种指针的例子:
- 指向int型变量的指针:int* p = new int(0);
- 指向对象的指针:Widget* p = new Widget();
- 指向函数的指针:int (*p)(int n) = factorial; 这里p是指向已经定义的函数factorial的函数指针。
- 指向指针的指针:Widget** pp = &p;
- 可以指向任何类型的指针:void* any = NULL; 这里的any是指向任何类型变量的指针。比如any = p; 或是any = pp; 甚至any = &pp; 都是合法的。
对于#1和#2的情况,大家应该已经比较熟悉,这里就不赘述了。
对于函数指针,在C语言实现动态方法绑定中非常有用。在C++中,更多的是通过接口虚函数覆盖(override)来代替。在某些兼容C程序动态行为绑定的例子中,可以采用函数指针。
比如我们可以根据用户输入的命令,定义对应的处理函数:
typedef bool (*command_process)(int argc, char** argv); bool start(int argc, char** argv) { // implementations } bool stop(int argc, char** argv) { // implementations } bool write(int argc, char** argv) { // implementations } bool read(int argc, char** argv) { // implementations } // assign processors to names std::map<std::string, command_process> processors; processors["start"] = start; processors["stop"] = stop; processors["write"] = write; processors["read"] = read; // use processors to process commands std::string command = "start"; processors[command](argc, argv); // 调用名称“start”对应的处理函数,实现动态效果
比较有意思的是可以指向任何类型的变量的指针,比如:
// 为了简单,这些变量全部定义在栈上,其实可以是new出来的 int n = 256; double x = 3.14159265; Widget w; // 定义一个任意指针 void* any = NULL; // 这个指针可以指向int类型的变量 any = &n; // 也可以指向double型 any = &x; // 或者是一个对象 any = &w; // 但是,如果想对该指针解引用,则必须用reinterpret_cast Widget* pw = reinterpret_cast<Widget>(any); // 然后就可以调用了,安全性需要程序员自己保证,如果类型不对,程序可能crash。 pw->widgetMethod();
所以,除非非常必要,不要使用任意指针。
指针是可以偏移解引用的,即使这个指针不是指向一个数组头部:p[i]等价于*(p+i)。这里的i不必是非负整数,还可以是负整数。比如p[-4]是合法的。
相同类型的指针之差是合法的,比如
int* begin = new int[100]; int* end = begin+100; int size = end - begin; // 这是合法的,size = 100,表示begin和end之间的变量的个数