1. 程式人生 > >C++函式指標、函式物件與C++11 function物件對比分析

C++函式指標、函式物件與C++11 function物件對比分析

  1. 函式指標怎麼宣告?能用來做什麼?什麼時候用?

  2. 函式指標變數名稱一定要和函式名字一樣嗎?一個函式只能定義一個函式指標嗎?

  3. 給函式指標變數初始化,獲取函式的地址時,有幾種方式?可以不加取址&符號嗎?想要傳入另外一個函式,一定要提前定義嗎?

  4. 函式物件的實質是什麼?怎麼理解這個東西?

  5. 怎麼呼叫函式物件?

  6. 函式物件和函式指標的比較?

  7. C++11中的function函式物件又是什麼,是用來幹什麼的?

1.函式指標

指向函式地址的指標變數,

在C編譯時,每一個函式都有一個入口地址,那麼這個指向這個函式的函式指標便指向這個地址。

函式指標主要由以下兩方面的用途:

呼叫函式和用作函式引數。

 宣告方法:

函式返回型別 + (指標變數名)+ (形參列表)

//例子
int func(int x);
int (*func) (int x);

使用例子:

#include<iostream>
#include<cstdlib>
#include<vector>
using namespace std;
 
int AddFunc(int a, int b)  
{  
    return a + b;  
}  

int main()
{
    int (*Add1)(int a,int b);
    int (*Add2)(int a,int b);

    Add1 = &AddFunc;
    Add2 = AddFunc;//兩種函式指標賦初值方法,可以加取地址符也可以不加

    cout << (*Add1)(3,2)<<endl; // 5
    cout<<Add1(3,2)<<endl;//輸出可以加*,也可以不加

    system("pause");
    return 0;
}

2.函式物件

 C++函式物件實質上是操作符過載,實現了對()操作符的過載。C++函式物件不是函式指標。但是,在程式程式碼中,它的呼叫方式與函式指標一樣,後面加個括號就可以了

函式物件例子

int AddFunc(int a, int b)  
{  
    return a + b;  
}  

class Add
{
public:
    const int operator()(const int a,const int b)
    {
        return a+b;
    }
};

int main()
{
    //函式指標
    int (*Add1)(int a,int b);
    int (*Add2)(int a,int b);
    Add1 = &AddFunc;
    Add2 = AddFunc;//兩種函式指標賦初值方法
    cout << (*Add1)(3,2)<<endl; // 5
    cout<<Add1(3,2)<<endl;//輸出可以加*,也可以不加
 
    //函式物件
    Add addFunction;
    cout<<addFunction(2,3)<<endl;//呼叫類的過載操作符()方法
    system("pause");
    return 0;
}

函式物件與函式指標比較

函式物件可以把附加物件儲存在函式物件中是它最大的優點

另外,C++函式物件還有一個函式指標無法匹敵的用法:可以用來封裝類成員函式指標。

它的弱勢也很明顯,它雖然用起來象函式指標,但畢竟不是真正的函式指標。在使用函式指標的場合中,它就無能為力了。例如,你不能將函式物件傳給qsort函式!因為它只接受函式指標。

3.C++11 function函式物件 

介紹:類模版std::function是一種通用、多型的函式封裝。std::function可以對任何可以呼叫的實體進行封裝,這些目標實體包括普通函式、Lambda表示式、函式指標、以及其它函式物件等。std::function物件是對C++中現有的可呼叫實體的一種型別安全的包裹(我們知道像函式指標這類可呼叫實體,是型別不安全的)。 
通常std::function是一個函式物件類,它包裝其它任意的函式物件,被包裝的函式物件具有型別為T1, …,TN的N個引數,並且返回一個可轉換到R型別的值。std::function使用 模板轉換建構函式接收被包裝的函式物件;特別是,閉包型別可以隱式地轉換為std::function。 
也就是說,通過std::function對C++中各種可呼叫實體(普通函式、Lambda表示式、函式指標、以及其它函式物件等)的封裝,形成一個新的可呼叫的std::function物件;讓我們不再糾結那麼多的可呼叫實體。一切變的簡單粗暴。

C++11 function物件示例:

#include <functional>
#include <iostream>
using namespace std;
 
std::function< int(int)> Functional;
 
// 普通函式
int TestFunc(int a)
{
    return a;
}
 
// Lambda表示式
auto lambda = [](int a)->int{ return a; };
 
// 仿函式(functor)
class Functor
{
public:
    int operator()(int a)
    {
        return a;
    }
};
 
// 1.類成員函式
// 2.類靜態函式
class TestClass
{
public:
    int ClassMember(int a) { return a; }
    static int StaticMember(int a) { return a; }
};
 
int main()
{
    // 普通函式
    Functional = TestFunc;
    int result = Functional(10);
    cout << "普通函式:"<< result << endl;
 
    // Lambda表示式
    Functional = lambda;
    result = Functional(20);
    cout << "Lambda表示式:"<< result << endl;
 
    // 仿函式
    Functor testFunctor;
    Functional = testFunctor;
    result = Functional(30);
    cout << "仿函式:"<< result << endl;
 
    // 類成員函式
    TestClass testObj;
    Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1);
    result = Functional(40);
    cout << "類成員函式:"<< result << endl;
 
    // 類靜態函式
    Functional = TestClass::StaticMember;
    result = Functional(50);
    cout << "類靜態函式:"<< result << endl;
 
    return 0;
}

對於各個可呼叫實體轉換成std::function型別的物件,上面的程式碼都有,執行一下程式碼,閱讀一下上面那段簡單的程式碼。總結了簡單的用法以後,來看看一些需要注意的事項:

關於可呼叫實體轉換為std::function物件需要遵守以下兩條原則: 
(1)轉換後的std::function物件的引數能轉換為可呼叫實體的引數; 
(2)可呼叫實體的返回值能轉換為std::function物件的返回值。 
std::function物件最大的用處就是在實現函式回撥,使用者需要注意,它不能被用來檢查相等或者不相等,但是可以與NULL或者nullptr進行比較。

function物件好處

std::function實現了一套型別消除機制,可以統一處理不同的函式物件型別。以前我們使用函式指標來完成這些;現在我們可以使用更安全的std::function來完成這些任務。

附加:

void visit(T &);
void visit(T &t)
{
    //遍歷功能
    return;
}

//利用函式指標機制,只讀(訪問)或區域性性修改,傳入一個函式作為引數(使用過程中自己定義)
template <typename T>
void Vector<T>::traverse(void(*visit)(T&)) //引數為函式指標,可以不用提前定義函式指標變數
{
	for (int i = 0; i < _size; i++)
	{
		visit(_elem[i]);//引數為向量元素的引用,通過該函式可直接訪問或修改向量元素
	}
}

template <typename T>
template <typename VST> //操作器
void Vector<T>::traverse(VST& visit) //利用函式物件機制,可全域性性修改
{
	for (int i = 0; i < _size; i++)
		visit(_elem[VST]);//什麼意思?
}