C ++:将结构成员视为数组

时间:2012-07-08 18:22:28

标签: c++ pointers types rtti

我有一个包含许多(如数百个)指针的结构。每个指针都是不同的类型,但它们都从一个公共基类继承 - 让我们称它为Base。我正在使用多重继承。 (这都是机器生成的,这就是为什么它很奇怪。)

e.g:

class Data {
    Class1* p1;
    Class2* p2;
    Class3* p3;
    ...etc...
};

我想在所有这些上调用Base中定义的方法。我可以生成这样的代码:

void callFooOnData(Data* p)
{
    if (p1) p1->Foo();
    if (p2) p2->Foo();
    if (p3) p3->Foo();
    ...etc...
}

问题是,我有很多和很多这些,十亿种不同的数据,而上述代码最终会非常大并影响我的足迹。所以我试图用更聪明的东西取代它。

在C中,我可以简单地获取第一个结构成员的地址和计数,并执行以下操作:

void callFooOnData(Data* p)
{
    callFooOnObjects(&p1, 3);
}

但是,当然,我不能在C ++中这样做,因为结构成员不是统一类型,并且将它们转换为Base *可能涉及更改它们,这可能涉及更改指针,因为它们'不是一个统一的类型,每个成员必须对指针进行不同的更改,我不能这样做。

有没有办法做这样的事情是C ++?这是所有机器生成的代码,所以它不一定非常漂亮,谢天谢地 - 鲨鱼已经被跳过了 - 但我确实需要它尽可能便携......

(我确实可以访问RTTI,但如果可能出于性能原因,我希望避免使用它。)

更新

所以,据我所知......你不能用C ++做到这一点。根本无法做到。我在C语言中完全直截了当地被误导了。在C ++中,如果你有一个很好的类型指针,你只能安全地在类层次结构中的两个类型之间转换指针,当然,我不会。 / p>

所以我将不得不改变底层问题以避免这种情况:我提出的解决方案是将结构中的每个指针存储两次,一次存在于Base *的数组中以进行迭代,并且一次作为一个ClassWhatever *用于调用方法。当然,这很糟糕,因为它会使数据结构的大小翻倍。

所以,如果有人想证实这一点(我希望被证明是错误的),我很乐意将他们的答案标记为正确......

6 个答案:

答案 0 :(得分:4)

  

每个指针都是不同的类型,但它们都是从公共基类继承的 - 让我们称之为Base

不要拥有数百个成员,只需保留Base类指针的容器:

class Data {
   std::vector<Base*> objects;
};

在一个好的设计中,你并不需要知道每个对象的类型,如果它被抽象出来,它实际上会更好。记住,对接口进行编程总是好的,而不是具体的类。

  

并将它们转换为Base *可能涉及更改它们

实际上,对于public继承,强制转换应该是隐式的。

如果您只对所有这些方法调用Foo()Foo()可以是基类中的virtual方法,这样就可以充分利用多态性。

答案 1 :(得分:0)

  

但是,当然,我不能在C ++中这样做,因为结构成员   不是统一的类型,并将它们转换为Base *可能涉及改变   它们,这可能涉及改变指针,因为它们是   不是统一的类型,指针必须以不同的方式改变   对于每个成员,我无法做到。

虽然这是一个坏主意,但事实并非如此: 您可以使用指针算法迭代指针,就像在C中一样。指针大小相同,并且它们有一个共同的基类(不考虑结构对齐问题等)。它很脏但从技术上说它很有效。如果您将实例本身作为类中的成员而不是指向它们的指针,则会有所不同。

C ++ 11中最好的方法是使用

std::vector< std::unique_ptr<Base> > objects;

请参阅http://www.drdobbs.com/cpp/c11-uniqueptr/240002708

答案 2 :(得分:0)

您必须通过自动生成代码来“对抗”自动生成的代码。您可以使用ctags之类的工具来“解析”自动生成的类,并从ctags输出中自动生成代码。请参阅http://ctags.sourceforge.net/ctags.html

您也可以尝试将数据转换为元组,如下所述,可能重复,问题:Iterate through struct variables

我不确定哪个更快......

如果您可以修改自动生成此源代码的工具 - 也许最好是扩展此工具...

答案 3 :(得分:0)

另一个选项(不可编译):

class DataManipulator 
{
public:
   DataManipulator(const Data& aData_in)
   {
      objects.add(aData_in->Class1);
        .
        .
   }
   void operate()
   {
       for(objects_iterator...)
       {
           if(*objects_iterator != NULL)
                 objects_iterator->Foo();
       }
   }
private:

   std::vector<Base*> objects;
};

答案 4 :(得分:0)

我不想改变我以前的答案。但是我找到了另一个适合你的解决方案:完整示例:http://ideone.com/u22FO

以下主要部分:

struct C {
  A1* p1;
  A2* p2;
  A3* p3;
  A4* p4;
  // ...
};

template <class D, size_t  startNumber, size_t numMembers, bool notFinished>
struct FooCaller;
template <class D, size_t  startNumber>
struct FooSingleCall;

template <class D, size_t  startNumber, size_t numMembers>
struct FooCaller<D, startNumber, numMembers, false> {
   void operator() (D& d) {}
};

template <class D, size_t  startNumber, size_t numMembers>
struct FooCaller<D, startNumber, numMembers, true> {
   void operator() (D& d) {
       FooSingleCall<D,startNumber>()(d);
       FooCaller<D, startNumber + 1, numMembers, startNumber < numMembers>()(d);
   }
};

#define FooSingleCallD(n) \
template <class D> \
struct FooSingleCall<D,n>{ \
   void operator() (D& d) { \
       d.p##n->foo(); \
   } \
}

FooSingleCallD(1);
FooSingleCallD(2);
FooSingleCallD(3);
FooSingleCallD(4);
// ... unfortunately repeat as many times as needed

template <class D, size_t numMembers>
void callFoo(D& d)
{
   FooCaller<D, 1, numMembers, 1 <= numMembers>()(d);
}

意识到:要足够聪明,不要定义数百个FooSingleCall ...请参阅这个着名问题的答案之一:https://stackoverflow.com/a/4581720/1463922,您将看到如何在5行中制作1000个实例......

另外,请将FooSingleCall替换为从您的班级检索N指针的东西 - 比如GetNPointer ......

答案 5 :(得分:-1)

所有指针都是相同的大小(C ++中的所有类对象指针都是相同的大小),实际上编译器需要相当不正常,以便在此指针列表中的任何位置插入填充。无论如何,可能的填充问题与C中的相同。所以你可以像在C中那样做,完全没问题。

void foo( Base const* );

void bar()
{
    // ...
    foo( *(&thingy.p1 + 3) );
}

就这么简单。

也就是说,即使使用机器生成的代码,设计听起来也很糟糕,错误,非常糟糕。

在生成vtable时(其中每个指针指向一般不同的签名函数),确实有类似的东西,但这种情况非常罕见。所以,这听起来像一个XY问题。就像,你正试图解决问题X,想出了一个解决方案Y,现在正在问想象解决方案Y而不是真正的问题X ...