C++里怎样定义递归函数指针

在C++里说到函数指针,有很多人都是避而远之,更别说什么“递归函数指针”了。但是实际上有的东西越是神秘,其原理反而越简单,所以我这里就先卖一个关子,假装介绍一个“高深的技巧”一样做这个开场白了。
什么是“函数指针”呢?(别砸我!我想从头开始讲)比如我们定义了一个函数:

int f(char);

 

我们先不管它的实现,我们只知道它的申明,那么我们就可以定义一个函数指针fp来指向它:

FuncPtr fp = f; //或:fp = &f;老实说,这2种写法有什么不同,我也不知道
fp();  //运行f函数

注意C++是强类型安全的,也就是说,如果fp不是和f同类型的话,是不能赋值的,即使它们占用的存储空间一样大,甚至都是32位的整数(在别的系统里可能不同)。比如:

void g(char);
fp = g; //Error:type mismatch!

那么fp是什么类型的呢?

typedef int (* FuncPtr)(char);

fp的类型是FuncPtr。我这里只是简单的介绍,为后面的讨论做铺垫。如果你对这些代码有什么不懂的,我建议你还是看看专门的C++入门书籍。
现在我开始介绍“递归函数指针”。想象一下,如果一个函数能返回自己的指针,那么这个函数应该怎样定义呢?

FuncPtr fp = f(/*whatever*/); //运行f函数,得到函数指针
fp = fp(/*whatever*/);  //运行f函数,得到函数指针
fp = fp(/*whatever*/);  //运行f函数,得到函数指针
//...and so on

我们现在是在C++里,不是在C或是汇编里,在那些语言里没有类型检查,只要是存储空间一样大,什么都可以相互等同。在C++里,我们享受了类型安全的优点,现在该修补它带来的一些“缺陷”来当作回报了。也许有人很快会在脑海里闪过一些代码,类似于:

typedef FuncPtr (* FuncPtr)(/*whatever*/); //对应的函数指针的定义

FuncPtr f(/*whatever*/)  //一个函数的定义
{
    //do something...
    return f;  //返回函数的指针
}

这段代码显然是有问题的,原因不用我说,那么该怎么解决这个问题呢?我们把目光集中到了临时变量上。对于return by value(没找到合适的汉语对应)的C++函数,其返回值都是靠临时变量来传送的。比如一个函数function:

TypeA function() //return type is TypeA
{
    TypeA a;
    return a; //value type is TypeA
}

编译器会产生这样的等同代码:

void function(TypeA _tmp_)
{
    TypeA a;
    _tmp_ = a; //these are for "return a;"
    return;
}

注意到函数的返回类型(_tmp_的类型)和a的类型是一样的,都是TypeA。那么能不能使_tmp_和a的类型不同呢?当然可以了!只要a能转化成_tmp_,或说_tmp_能从a构建出来,比如:

class TypeA{};
class TypeB
{
    TypeB(){}
    TypeB(const TypeA &){} //construct B from A
};

从一个typeA类型的对象可以构建一个TypeB类型的对象,那么我们可以这样修改上面的函数:

TypeB function()
{
    TypeA a;
    return a; //construct B-object from a
}

我们讨论到这里,也许有人会想到我下一步要做什么了!对了,由于在函数申明中,函数返回的值类型不能是函数本身的指针类型——不能简单的递规typedef,这在前面的例子里大家都看到了——那么我们需要一个类型转化:从函数本身的指针类型转化成另一个类型,再在赋值的时候转化回来,就可以了。于是我们定义的了一个类:

class _FuncPtrClass
{
    //something magic here...
};

_FuncPtr f(/*whatever*/) //函数f定义
{
    //do something...
    return f;
}
typedef _FuncPtrClass (* FuncPtr)(/*whatever*/); //函数指针类型FuncPtr定义

我们来看看_FuncPtrClass类应该具有哪些特点。首先,它要能从FuncPtr对象构建出来,并且要负责传递这个指针的值,所以它需要一个FuncPtr类型的成员变量,和一个定制的构造函数。注意,FuncPtr的定义是在_FuncPtrClass之后的,所以实际上在_FuncPtrClass内应该重新定义FuncPtr:

class _FuncPtrClass
{
public:
    typedef _FuncPtrClass (* FuncPtrInClass)(/*whatever*/); //重新定义FuncPtr为FuncPtrInClass
    _FuncPtrClass(FuncPtrInClass f):f_(f){}   //定制的构造函数
private:
    FuncPtrInClass f_;     //FuncPtrInClass类型的成员变量
};

现在函数f的定义就是完全合法的了,并且可以成功的运行。不过,好像还缺点儿什么——怎么样得到临时变量_FuncPtrClass-object里的函数指针的值呢?是的,我们还缺一个转换的函数,这可以通过一个简单的operator做到:

class _FuncPtrClass
{
//...
public:
    typedef _FuncPtrClass (* FuncPtrInClass)(/*whatever*/);
    _FuncPtrClass(FuncPtrInClass f):f_(f){}
    operator FuncPtrInClass(){return f_;}   //从_FuncPtrClass到FuncPtrInClass转换的函数
private:
    FuncPtrInClass f_;
};

这就是我们最终版本的_FuncPtrClass,配合上:

_FuncPtr f(/*whatever*/)
{
    //do something...
    return f;
}
typedef _FuncPtrClass (* FuncPtr)(/*whatever*/);

我们就能放心的运行以前的代码了:

FuncPtr fp = f(/*whatever*/); //运行f函数,得到函数指针
fp = fp(/*whatever*/);  //运行f函数,得到函数指针
fp = fp(/*whatever*/);  //运行f函数,得到函数指针
//...and so on

好了,大功告成!
也许有人会认为我这里讲的例子一点实际用处也没有,的确,我也这样认为。不过我并不认为这个技巧是没用的。任何人说某个东西一点用处也没有,都有点武断的嫌疑。我所阐述的并不是这样的一个例子,而是这样的一种技巧,我也是从别的书上看过来的,但是我并不认为那位作者是在讲一个毫无用处的例子。
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页