2011-08-22 55 views
7

假设我有许多成员对象:C++:迭代所有对象的成员?

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

是否有短/简洁的方式做这样的事情:

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

而是单独访问每个人的?

我想我可以把它们放在一个矢量中,并使用shared_ptr来达到同样的效果,我只是想知道如果说,Boost或其他一些流行的库没有自动执行此操作的东西。

+2

我真的不认为这是一个好主意。如果你需要迭代一些东西,然后把它声明为一个可迭代的容器,或者你为什么不这样做? – leftaroundabout

+0

检出boost序列化 –

+1

C++没有反射。这意味着这是不可能的。 –

回答

11

C++不支持类自省,所以你不能遍历类中的所有成员 - 不是没有一个(手动编写)函数遍历所有成员的函数。

你可以,原则上,增加一个模板成员像这样:

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

这就是说,我会认为这是一个破碎的设计;你已经离开并公开所有内部成员。如果您稍后添加一个,会发生什么?或者改变一个的语义?创建一个有时过时的缓存值?等等。而且,如果你有不全部从相同类型继承的成员,会发生什么?

退一步,重新考虑你的设计。

-2

这是不可能与C++,如果你真的真的需要这个,然后使用C#。 但我真的怀疑你这么做。

话虽如此,你真正拥有的唯一选择是使用.NET并使用微软称为托管C++/CLI的托管C++。但要注意的是,你的班级必须是一个托管的'ref class',这意味着一个托管班级。最终它被全部编译成MSIL,并且是语言不可知的中间语言。这是您可以在运行时使用.NET反射来发现它的成员函数和值的唯一方法。但是,即使使用.NET,也不像描述如何使用它那么简单。反思的另一个缺点是速度很慢,所以如果你使用它,你会有一个糟糕的设计。 反思很好,因为.NET使用它来帮助序列化数据类型,这使得XML序列化非常易于使用。

3

如果您按规则进行游戏并使用模板元编程,C++可以执行此类操作。

而不是存储在一个结构或类你的东西的,它存储在一个元组:

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

然后你可以使用模板元编程算法等等(见Boost.Fusion)来访问成员和戏弄东东。您可以在元组的元素上迭代模板样式。

0

您可能在此寻找的是访客模式。如果您描述的对象有许多不平凡的成员字段,并且您发现有许多不同的函数都以相同的方式遍历此数据结构,那么访问者模式可以非常有助于减少代码重复量。它不是自动的,因为你必须编写遍历所有成员字段的函数,但你只需要做一次,而且你可以多次使用不同的访问者类来完成不同的事情。

Visitor模式不涉及编写相当多的代码,你需要为游客抽象基类:

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); 
} 

最后,你需要实现的具体访问者类是做你感兴趣的,在实际工作中一般相当于从收集的数据结构信息,在更改数据结构,或两者的组合。谷歌访客模式,你会发现很多的帮助。

6

这个问题有几种解决方案,与反对者的说法相反,但没有内置方式。

C++在编译时支持一种有限的自省:您可以检查模板参数。

使用Boost.TupleBoost.Fusion(对于它的地图),您确实可以实现您的愿望。在Boost.Fusion中,甚至可以使用BOOST_FUSION_ADAPT_STRUCT将基本结构转换为融合序列(从而迭代它)。

虽然它需要相当多的模板元编程。

+0

喜欢这个解决方案。在此处发布详细代码:[C++利用boost fusion adapt_struct迭代嵌套的struct字段](http://stackoverflow.com/q/12084781/362754) – minghua

0

这是我的方式来做你想在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); 
}