簡體   English   中英

C ++:迭代所有對象的成員?

[英]C++: Iterating through all of an object's members?

假設我有一個包含許多成員的對象:

class Example {
    AnotherClass member1;
    AnotherClass member2;
    YetAnotherClass member3;
    ...
};

是否有簡短的方法來做類似的事情:

foreach(member m: myExample)
    m.sharedMethod();

而不是單獨訪問每一個?

我想我可以將它們放在一個向量中並使用shared_ptr獲得相同的效果,我只是想知道是否說,Boost或其他一些流行的庫沒有自動執行此操作。

C ++不支持類內省,所以你不能迭代類中的所有成員 - 不管沒有(手動編寫)函數,無論如何都會迭代所有成員。

原則上,您可以像這樣添加模板成員:

template<typename Functor>
void doAllMembers(Functor &f) {
  f(member1);
  f(member2);
  f(member3);
}

也就是說,我認為這是一個破碎的設計; 你已經離開並公開了所有內部成員。 如果您稍后添加一個會怎么樣? 或者改變一個語義? 使一個緩存的值有時會過時? 等等,如果您的成員並非都從相同的類型繼承,會發生什么?

退一步重新考慮你的設計。

這個問題有幾種解決方案,與反對者的言論相反,但沒有內置方式。

C ++在編譯時支持有限的內省:您可以檢查模板參數。

使用Boost.TupleBoost.Fusion (對於它的地圖),你確實可以實現你想要的。 在Boost.Fusion中,您甚至可以使用BOOST_FUSION_ADAPT_STRUCT將基本結構轉換為Fusion序列(從而迭代它)。

它需要相當多的模板元編程。

如果你按照規則玩並使用模板元編程,C ++可以做這樣的事情。

而不是將您的東西存儲在結構或類中,而是將其存儲在元組中:

typedef boost::tuple<AnotherClass, AnotherClass, YetAnotherClass> Example;

然后你可以使用模板元編程算法等(參見Boost.Fusion)來訪問成員並捅東西。 您可以在元組的元素上迭代,模板樣式。

這是我想要在C ++ 11中實現的目標。 它使用元組模板 它不簡短,但如果你將一些代碼包裝在頭文件中是可以接受的。 有我完整的可編輯示例:

#include <iostream>
#include <string>
using namespace std;

#include <tuple>

//Our iteratation code

//first a iteration helper structure
template<int N = 0>
struct IterateP {
  template<class T>
  typename std::enable_if<(N < std::tuple_size<T>::value), void>::type
  iterate(T& t) {
    std::get<N>(t).sharedMethod(); //there is the method name
    IterateP<N+1>().iterate<T>(t);
  }

  template<class T>
  typename std::enable_if<!(N < std::tuple_size<T>::value), void>::type
    iterate(T&) {}
};

//wrapper of the helper structure for a more comfortable usage
template <typename T>
void iterate(T& t) { IterateP<>().iterate(t.members); } //look at the .members, is the name of the class tuple

//Helper notation macro. 
#define MEMBER(name, i) std::tuple_element<i,decltype(members)>::type &name = std::get<i>(members)

//YOUR CLASSES

struct AnotherClass {
  int value;
  void sharedMethod() { cout << value << endl; }
};

struct YetAnotherClass {
  string value;
  void sharedMethod() { cout << value << endl; }
};


//The class with iterable members
struct Example {
  std::tuple<AnotherClass, AnotherClass, YetAnotherClass> members; //members must be in a tuple

  //These are helper member definition to access the tuple elements as normal members (instance.member1)
  //If you don't you this you need to access the members with tuple classes
  MEMBER(member1, 0); //first param is the member name and the second is it's position in the tuple.
  MEMBER(member2, 1);
  MEMBER(member3, 2);
};

//USAGE
int main() {

  Example example;

  //setting members just as a normal class 
  example.member1.value = 1;
  example.member2.value = 2;
  example.member3.value = "hola";

  //magic
  iterate(example);
}

您在這里尋找的是訪客模式。 如果你有一個描述的對象,有許多非平凡的成員字段,並且你發現你有許多不同的函數都以相同的方式遍歷這個數據結構,那么訪問者模式可以非常有助於減少代碼重復的數量。 它不是自動的,你必須編寫遍歷所有成員字段的函數,但是你只需要執行一次,並且可以使用不同的訪問者類多次使用它來執行不同的操作。

訪問者模式確實涉及編寫相當多的代碼,您需要為訪問者提供一個抽象基類:

class VisitorBase 
{
    virtual void enter(Example& e)=0;
    virtual void leave(Example& e)=0;
    virtual void enter(AnotherClass& e)=0;
    virtual void leave(AnotherClass& e)=0;
    etc ...
};

然后,您需要在將要訪問的所有類中接受函數:

void Example::accept( VisitorBase& visitor ) 
{
    visitor.enter(*this);
    member1.accept(visitor);
    member2.accept(visitor);
    member3.accept(visitor);
    visitor.leave(*this);
}

最后,您需要實現具體的訪問者類,這些訪問者類會執行您感興趣的實際工作,這通常相當於從數據結構中收集信息,更改數據結構或兩者的組合。 谷歌訪客模式,你會發現很多幫助。

對於C ++來說這是不可能的,如果你真的真的需要這個而不是使用C#。 但我非常懷疑你這樣做。

話雖如此,關於你真正擁有的唯一選擇是使用.NET並使用托管C ++,微軟稱之為托管C ++ / CLI。 但需要注意的是,你的班級必須是一個托管的'ref class',這意味着一個托管類。 最終它全部被編譯成MSIL,以及與語言無關的中間語言。 這是你在運行時使用.NET反射來發現它的成員函數和值的唯一方法。 然而,即使使用.NET,也不像你描述的那樣容易在上面使用它。 反思的另一個缺點是它很慢,所以如果你大量使用它,你的設計就會很糟糕。 反思很好,因為.NET使用它來幫助序列化數據類型,這使得XML序列化非常容易使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM