1. 程式人生 > >c++中的函式物件與函式指標學習

c++中的函式物件與函式指標學習

篇一、函式指標
函式指標:是指向函式的指標變數,在C編譯時,每一個函式都有一個入口地址,那麼這個指向這個函式的函式指標便指向這個地址。
函式指標的用途是很大的,主要有兩個作用:用作呼叫函式和做函式的引數(經常用於函式回撥中)
函式指標的宣告方法

資料型別標誌符 (指標變數名) (形參列表);  //函式返回值型別  (*指標變數名) (函式形參列表)

一般函式的宣告為: 
int func ( int x );
而一個函式指標的宣告方法為:
int (*func) (int x);
前面的那個(*func)中括號是必要的,這會告訴編譯器我們宣告的是函式指標而不是宣告一個具有返回型為指標的函式,後面的形參要視這個函式指標所指向的函式形參而定。
(func是一個函式指標,指向的函式是,返回值型別為int,引數為int)
然而這樣宣告我們有時覺得非常繁瑣,於是typedef可以派上用場了,我們也可以這樣宣告:
typedef int (*PF) (int x);  //簡化複雜的語法宣告
PF pf;  //PF表示一個函式指標型別,那麼變數pf就是函式指標,這和int a這樣的語法雷同

這樣pf便是一個函式指標,方便了許多。當要使用函式指標來呼叫函式時,func(x)或者  (*fucn)(x) 就可以了(原因是編譯器會為函式指標隱式地解引用),當然,函式指標也可以指向被過載的函式,編譯器會為我們區分這些過載的函式從而使函式指標指向正確的函式。

typedef void (*PFT) ( char ,int );
void bar(char ch, int i)
{
    cout<<"bar "<<ch<<' '<<i<<endl;
    return ;
}
PFT pft;
pft = bar;   //相當於pft = &bar;
pft('e',91); //相當於(*pft)('e',91);

例子中函式指標pft指向了一個已經宣告的函式bar(),然後通過pft來實現輸出字元和整型的目的。 函式指標另一個作用便是作為函式的引數,我們可以在一個函式的形參列表中傳入一個函式指標,然後便可以在這個函式中使用這個函式指標所指向的函式,這樣便可以使程式變得更加清晰和簡潔,而且這種用途技巧可以幫助我們解決很多棘手的問題,使用很小的代價就可獲得足夠大的利益(速度+複雜度)。
typedef void (*PFT) ( char ,int );
void bar(char ch, int i)
{
    cout<<"bar "<<ch<<' '<<i<<endl;
    return ;
}
void foo(char ch, int i, PFT pf)
{
    pf(ch,i);
    return ;
}
PFT pft;
pft = bar;
foo('e',12,pft);   //函式名作為引數傳遞時候,退化為函式指標,編譯器起得作用

篇二、函式物件

前面是函式指標的應用,從一般的函式回撥意義上來說,函式物件和函式指標是相同的,但是函式物件卻具有許多函式指標不具有的有點,函式物件使程式設計更加靈活,而且能夠實現函式的內聯(inline)呼叫,使整個程式實現效能加速。(維基---兩個優點:1.編譯器可以內聯執行函式物件的呼叫;2函式物件內部可以保持狀態。)
函式物件:這裡已經說明了這是一個物件,而且實際上只是這個物件具有的函式的某些功能,我們才稱之為函式物件,意義很貼切,如果一個物件具有了某個函式的功能,我們變可以稱之為函式物件。(維基---物件允許被當作普通函式來呼叫。)
如何使物件具有函式功能呢,很簡單,只需要為這個物件的操作符()進行過載就可以了,如下:

class A{
    public:
    int operator()(int x){return x;}  //過載()操作符
};
A a;
a(5);  //物件作為函式呼叫

這樣a就成為一個函式物件,當我們執行a(5)時,實際上就是利用了過載符號()。
函式物件既然是一個“類物件”,那麼我們當然可以在函式形參列表中呼叫它,它完全可以取代函式指標!(呼叫函式指標和呼叫函式物件的語法是完全一致的)如果說指標是C的標誌,類是C++特有的,那麼我們也可以說指標函式和函式物件之間的關係也是同前者一樣的!(雖然有些嚴密)。當我們想在形參列表中呼叫某個函式時,可以先宣告一個具有這種函式功能的函式物件,然後在形參中使用這個物件,他所作的功能和函式指標所作的功能是相同的,而且更加安全。

下面是一個例子:

class Func{
public:
    int operator() (int a, int b)
    {
        cout<<a<<'+'<<b<<'='<<a+b<<endl;
        return a;
    }
};
int addFunc(int a, int b, Func& func)
{
    func(a,b);
    return a;
}
Func func;
addFunc(1,3,func);

上述例子中首先定義了一個函式物件類,並重載了()操作符,目的是使前兩個引數相加並輸出,然後在addFunc中的形參列表中使用這個類物件,從而實現兩數相加的功能。 如果運用泛型思維來考慮(用模板形參T來替換實際的形參型別int、double等),可以定一個函式模板類,來實現一般型別的資料的相加:
class FuncT{
public:
    template<typename T>
    T operator() (T t1, T t2)
    {
        cout<<t1<<'+'<<t2<<'='<<t1+t2<<endl;
        return t1;
    }
};
template <typename T>
T addFuncT(T t1, T t2, FuncT& funct)
{
    funct(t1,t2);
    return t1;
}
FuncT funct;
addFuncT(2,4,funct);
addFuncT(1.4,2.3,funct);

大名鼎鼎的STL中便廣泛的運用了這項技術,詳細內容可參見候捷大師的一些泛型技術的書籍,不要以為函式物件的頻繁呼叫會使程式效能大大折扣,大量事實和實驗證明,正確使用函式物件的程式要比其他程式效能快很多!所以掌握並熟練運用函式物件才能為我們的程式加分,否則.......

如此看來,函式物件又為C++敞開了一道天窗,但隨之而來的便是一些複雜的問題和陷阱,如何去蔽揚利還需要我們不斷學習和探索。

STL是C++的標準模板庫(standard template library),自然其中定義的都是模板。 相比於類和函式宣告的顯式介面(explicit interface),類模板和函式模板宣告的介面屬於隱式介面(implicit interface)。 因為模板引數應當滿足的介面是由模板中表達式的合法性決定的,這一點給了模板很大的自由。 而函式物件函式指標具有同樣的呼叫語法,因此STL中這兩者常常可以互換。更多關於隱式介面和顯式介面的概念和區別,參見Effective C++: Item 41

粉底部分為本人註釋

主要參見以下兩個部落格:

http://www.cnblogs.com/lvpengms/archive/2011/02/21/1960078.html

http://harttle.com/2015/07/03/stl-function-objects-and-pointers.html

關於STL中的函式物件和函式指標的進一步深入學習使用,可以參看下面的網址:

http://www.cplusplus.com/reference/functional/

http://www.myexception.cn/cpp/1994728.html(沒有英文基礎,可以先看這個裡面的函式介紹例子,然後再看上面參考文件)