2010-11-17 123 views
8

我已經寫以下代碼:C++ - 約函數指針問題

#include "stdafx.h" 
#include <iostream> 
using namespace std; 

double funcA() 
{ 
    return 100.0; 
} 

int g(double (*pf)()) 
{ 
    cout << (*pf)() << endl; 
    return 0; 
} 

int g2(double pf()) 
{ 
    cout << pf() << endl; 
    return 0; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    g(&funcA); // case I 
    g(funcA); // case II 

    g2(funcA); // case III 
    g2(&funcA); // case IV 
    return 0; 
} 

我已經運行上VS2008上面的代碼,並且每個函數調用返回「100」。 這裏是問題:

Q1>代碼中是否有任何問題?

Q2>似乎C++在* pf和pf之間沒有區別。那是對的嗎?

謝謝

+1

+1對於一個非常明確的問題。 – 2010-11-17 16:31:59

+0

你也可以用g的聲明和g2的body一起使用 – UncleZeiv 2010-11-17 16:33:55

+1

我建議本教程關於函數指針:http://www.newty.de/fpt/index.html ..它非常全面 – Jack 2010-11-17 16:34:36

回答

8

C++ 確實實際上區分了double()double(*)()兩種類型,但區別很微妙。當您將函數類型作爲參數傳遞給函數時,函數類型會自動「降級」爲函數指針。 (這是類似的,我想,對數組類型如何降解爲當作爲函數參數傳遞的指針類型。)

然而,功能類型和函數指針型仍然不同類型,根據C++類型系統。考慮以下情況:

void g() { } 

template <class F> 
struct Foo 
{ 
    Foo(const F& f) : func(f) 
    { } 

    void operator()() { func(); } 

    F func; 
}; 


int main() 
{ 
    Foo<void()> f(g); 
    f(); 
} 

由於無法將函數類型聲明爲自動變量,因此應該編譯失敗。 (請記住,函數不是C++中的第一類對象。)所以聲明F func;是無效的。然而,如果我們改變我們main功能,使用函數指針代替實例化模板,像這樣:

int main() 
{ 
    typedef void(*function_pointer)(); 
    Foo<function_pointer> f(g); 
    f(); 
} 

...現在它編譯。

+3

+1爲堆疊式的助手提供幫助,這讓我內心的C++程序員微笑。 – 2010-11-17 16:49:53

+0

所以它有點像引用和指針之間的區別? – joshperry 2010-11-19 01:21:02

+0

請注意,如果您要執行typedef void F(),那麼'F func'會有效。 struct A {F func; };(而類定義將等同於'struct A {void func();};')。它會在代碼中導致錯誤,因爲'F'是依賴的,並且禁止使用依賴類型聲明一個不使用函數聲明符('(params)'形式)的函數(approp。文本是14.3 0.1/3)。 – 2010-11-19 15:42:21

2

以下功能是相同的:

int g(double (*pf)()) 
{ 
    cout << (*pf)() << endl; 
    return 0; 
} 

int g2(double pf()) 
{ 
    cout << pf() << endl; 
    return 0; 
} 

解引用一個函數指針(如圖克)是一樣的調用該函數的名字。

Q2>似乎C++不會在* pf和pf之間產生 差異。那 是否正確?

* pf和pf(作爲變量)是有區別的。如果pf是一個函數,* pf和pf()是相同的(注意括號)。

+0

+1是正確的(與此處的幾個答案不同) – 2010-11-17 16:52:25

0

對於大多數現代編譯器,「(* variable)」沒有區別。和「變 - >」。但是,必須檢查正在使用的類是否覆蓋解除引用操作符。

許多程序員在定義函數指針時使用typedef,主要是爲了使讀取更容易。此外,double pf()語法可能易於出現可讀性錯誤,並且可能會在執行參數行上的函數時感到困惑。

+0

解除引用運算符是一個無關的錯誤。此外,你所說的是不真實的。無論是否(* a).b'等於'a-> b''與編譯器沒有任何關係 - 這只是運算符是否適合於'a'類型的過載問題。 – 2010-11-17 16:41:42

0

您發佈的代碼沒有問題或不同。但是,如果您正在編寫採用函數的模板,則應該使用g2中的語法。考慮以下幾點:

template<typename Iter, typename Func> 
void for_each(Iter begin, Iter end, Func functor) 
{ 
    for(; begin != end; ++begin) 
    { 
     functor(*begin); 
    } 
} 

需要注意的是,如果你functor之前把引用操作,你限制你寫的函數指針算法的效用。然而,如果你不把它放在那裏,有人可以傳遞一個STL函子,比如std::bind2nd返回的東西。

因此,我總體上建議在可能的情況下使用第二個(無*)語法。

0

考慮下面的代碼段

void pf(); 

void (&prf)() = pf; // OK, bind prf to pf 
void (&prf)() = &pf; // ill-formed, can't bind prf to an function pointer value 

在另一方面

void (*ppf)() = pf; // OK, function decays to a pointer 
void (*ppf)() = &pf; // OK, pointer assigned to a pointer 

所以存在從一個指針的函數(其被稱爲「衰減」)的隱式轉換。這也使得你可以說***...***pf--任意多次解引用它 - 在每一步中都會發生一個指針轉換函數,這個函數可以解除之前的解除引用的影響。

在函數的參數列表中,T f()T (*f)()是聲明一個參數

void f(void g()); // g has type void (*)() 
void f(void (*g)()); // g has type void (*)() 

的等同的方式(除了拼寫)的引用將抑制該參數類型調整

void f(void (&g)()); // g has *not* the type void (*)() 

這是與數組聲明的參數完全相同:參數從來都不是數組,但它們總是指針,如果它們被聲明爲數組。