天天看點

局部類實作C++的閉包

首先說明,雖然經常提到閉包,但我對閉包這個概念還真是不清晰,隐約感覺如果函數A中定義并傳回了函數B,而函數B在函數A之外仍然可以正常運作并通路函數A中定義的變量,同時函數A中定義的變量不能被外部通路,就叫閉包——如果這個了解錯了,那就當我啥也沒說!

看到有人寫博說通過C++11的新特性std::bind來實作閉包,仔細想了一下,其實通過C++03的兩個特性就可以實作的:一個是局部類,一個是靜态局部變量。

靜态局部變量

C++允許在函數内部定義靜态的局部變量,這些變量不會在離開函數時被銷毀,就像這樣

void func() { 

    static int i = 0; 

    cout << i++ << endl; 

如果多次調用func,會發現輸出到控制台的值一直在遞增。

局部類 

想在函數裡定義局部函數是不可能的,C++沒這個文法。但是可以在函數裡定義局部類,這個類隻能在該函數裡使用,很有意思的,就像這樣

    class LocalClass { 

    private: 

        // 定義私有成員 

    public: 

        void run() { 

            // ...... 

        } 

    } obj; 

    obj.run(); 

如果需要将局部類對象傳回到外面,就需要定義一個接口,用于接收傳回的對象并調用其中的方法;同時,在函數内需要使用new來生成對象并傳回其指針,就像這樣

class ILocal { 

public: 

    virtual void run() = 0; 

}; 

ILocal* func() { 

    class LocalClass: public ILocal { 

        virtual void run() { 

    }; 

    return new LocalClass(); 

基礎知識具備了,下面來實作閉包,過程就不說了,看注釋吧

#include <iostream> 

using namespace std; 

//////// 接口法實作 //////////////////////////////////////// 

// 定義一個函數對象接口……就像C#的委托 

class ITest { 

    // 定義這個運算符主要是為了像函數一樣調用,就像這樣:obj(); 

    void operator() () { 

        process(); 

    } 

protected: 

    // 接口函數 

    virtual void process() = 0; 

// 下面函數傳回一個ITest對象指針 

ITest* test() { 

    // 函數内的靜态變量,離開函數也不會銷毀,而且可以 

    static int count = 0; 

    // 定義一個局部類 

    class Test: public ITest { 

        // 實作ITest中定義的接口函數來實作操作 

        virtual void process() { 

            cout << "Count is " << count++ << endl; 

    // 傳回一個新的對象……這裡必須得new,你懂的 

    return new Test(); 

//////// 函數法實作 //////////////////////////////////////// 

// 定義測試函數指針類型 

typedef void (*Func)(const char*); 

// 下面函數傳回一個閉包中的函數 

Func testFunc() { 

    // 靜态局部變量初始化為100 

    static int count = 100; 

    // 靜态局部常量 

    static const char* const NAME = "James"; 

    // 不能直接定義函數,隻好定義局部類的靜态方法,并傳回其指針 

    class Test { 

        // 這個定義說明可以傳入參數,同理也可以有傳回值 

        static void process(const char* pName = NULL) { 

            if (pName == NULL) { pName = NAME; } 

            cout << pName << " Count is " << count-- << endl; 

    return Test::process; 

//////// 程式入口:主函數 ////////////////////////////////// 

int main(int argc, char* argv[]) { 

    ITest* pT = test(); 

    Func pF = testFunc(); 

    // 多次調用得從函數裡傳回出來的函數和對象,觀察結果 

    for (int i = 0; i < 10; i++) { 

        (*pT)(); 

        pF((i % 2 == 0) ? NULL : "Fancy"); 

給個運作結果

Count is 0 

James Count is 100 

Count is 1 

Fancy Count is 99 

Count is 2 

James Count is 98 

Count is 3 

Fancy Count is 97 

Count is 4 

James Count is 96 

Count is 5 

Fancy Count is 95 

Count is 6 

James Count is 94 

Count is 7 

Fancy Count is 93 

Count is 8 

James Count is 92 

Count is 9 

Fancy Count is 91 

C++是門非常靈活的語言,我們平時用到的相對于C++ Specification來說,僅僅是冰山一角。雖然因為工作關系我已經有四五年沒用它了,但它依然是我最喜歡的語言之一。

本文轉自邊城__ 51CTO部落格,原文連結:http://blog.51cto.com/jamesfancy/1166900,如需轉載請自行聯系原作者

繼續閱讀