2009-01-19 79 views
4

我有一個類有另一個類對象的向量作爲成員。在這個類的很多功能我必須做對Vector中的所有對象相同的操作:遍歷矢量和調用函數

class Small 
{ 
    public: 
    void foo(); 
    void bar(int x); 
    // and many more functions 
}; 

class Big 
{ 
    public: 
    void foo() 
    { 
     for (size_t i = 0; i < VectorOfSmalls.size(); i++) 
      VectorOfSmalls[i]->foo(); 
    } 
    void bar(int x) 
    { 
     for (size_t i = 0; i < VectorOfSmalls.size(); i++) 
      VectorOfSmalls[i]->bar(x); 
    } 
    // and many more functions 
    private: 
    vector<Small*> VectorOfSmalls; 
}; 

我要簡化代碼,並找到一種方法,不要重複在每一個功能去其他的載體。

我已經考慮過創建一個函數,它接收一個指向函數的指針,並調用向量的每個成員的指針函數。但我不確定在C++中使用函數指針是個好主意。

我也一直在考慮仿函數和functionoids,但它會迫使我爲每個函數創建一個類,它聽起來像是一種矯枉過正。

另一種可能的解決方案是創建一個接收一個字符串的函數,並調用命令根據字符串:

void Big::call_command(const string & command) 
{ 
    for (size_t i = 0; i < VectorOfSmalls.size(); i++) 
    { 
     if (command == "foo") 
      VectorOfSmalls[i]->foo(); 
     else if (command == "bar") 
      VectorOfSmalls[i]->bar(); 
    } 
} 
void Big::foo() 
{ 
    call_command("foo"); 
} 

但它可能工作慢(不需要創建一個字符串,而不是隻是一個函數調用的)如果函數具有不同的簽名,也會產生問題。

那麼你會推薦什麼?我應該離開現在的一切嗎?編輯:我只能使用STL而不是提升(舊編譯器)。

回答

16

那麼你可以重寫for循環使用迭代器和更多的STL是這樣的:

void foo() { 
    std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::foo)); 
} 

void bar() { 
    std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::bar)); 
} 

除此之外,你可以使用一些宏來避免重新輸入了很多,但我不是一個那是一個巨大的粉絲。就我個人而言,我喜歡使用命令字符串的單個函數。因爲它爲您提供了更多的決定權。

如果你確實需要一個參數來決定該怎麼做,我會使用一個枚舉和一個像這樣的開關,它比字符串和級聯if更高效。另外,在你的例子中,你有如果決定在循環內部做哪些事情。因爲「每個呼叫只需要決定一次」哪個命令「,所以檢查循環外部並且具有多餘的循環副本效率更高。 (注意:如果在編譯時已知該命令,則可以使該命令成爲模板參數,這聽起來就像是這樣)。

class Big { 
public: 
    enum Command { 
     DO_FOO, 
     DO_BAR 
    }; 

void doit(Command cmd) { 
    switch(cmd) { 
    case DO_FOO: 
     std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::foo)); 
     break; 
    case DO_BAR: 
     std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::bar)); 
     break; 
    } 
}; 

而且,正如你所說,這是相當瑣碎,以取代&小::什麼,有什麼成員函數指針,只是傳遞作爲參數。你甚至可以把它做成一個模板。

class Big { 
public: 
    template<void (Small::*fn)()> 
    void doit() { 
     std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(fn)); 
    } 
}; 

然後,你可以這樣做:

Big b; 
b.doit<&Small::foo>(); 
b.doit<&Small::bar>(); 

這個和常規參數方法兩個好處是,大的並不需要改變,如果你改變小有更多的程序!我認爲這是首選的方法。

如果你想能夠處理一個單一的參數,你就需要添加一個bind2nd過,這裏有一個完整的例子:

#include <algorithm> 
#include <functional> 
#include <iostream> 
#include <vector> 

class Small { 
public: 
    void foo() { std::cout << "foo" << std::endl; } 
    void bar(int x) { std::cout << "bar" << std::endl; } 
}; 


class Big { 
public: 
    template<void (Small::*fn)()> 
    void doit() { 
     std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(fn)); 
    } 

    template<class T, void (Small::*fn)(T)> 
    void doit(T x) { 
     std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::bind2nd(std::mem_fun(fn), x)); 
    } 
public: 
    std::vector<Small *> VectorOfSmalls; 
}; 

int main() { 
    Big b; 
    b.VectorOfSmalls.push_back(new Small); 
    b.VectorOfSmalls.push_back(new Small); 

    b.doit<&Small::foo>(); 
    b.doit<int, &Small::bar>(5); 
} 
+0

謝謝埃文。但std :: mem_fn是boost的一部分,而不是STL。也許有另一種方法來使用for_each在我的情況?那麼具有不同簽名的功能呢? – 2009-01-19 19:07:44

+0

oops,typo:mem_fn是boost的一部分,但std :: mem_fun是STL的一部分。 – 2009-01-19 20:26:49

4

如果你使用std庫,你應該看看for_each

您提到在C++中使用函數指針可能不是一個好主意,但是 - 讓您擔心的是速度 - 您必須在擔心之前查看它是否甚至是性能瓶頸區域。

0

嘗試boost::functionboost::bind

void Big::call_command(const boost::function<void (Small*)>& f) 
{ 
    for (size_t i = 0; i < VectorOfSmalls.size(); i++) 
    { 
     f(VectorOfSmalls[i]); 
    } 
} 

int main() 
{ 
    Big b; 
    b.call_command(boost::bind(&Small::foo, _1)); 
    b.call_command(boost::bind(&Small::bar, _1, 5)); 
}