繁体   English   中英

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

[英]C++: treating structure members as an array

我有一个包含很多(如数百个)指针的结构。 每个指针都是不同的类型,但是它们都继承自一个通用的基类---我们称之为Base。 我正在使用多重继承。 (这都是机器生成的,因此很奇怪。)

例如:

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 ++中,如果您要有一个类型良好的指针开始,则只能在类层次结构中的两种类型之间安全地转换指针,当然,我没有。

因此,我将不得不更改底层问题以避免这种情况:我想出的解决方案是将结构中的每个指针存储两次,一次存储在用于迭代的Base *数组中,一次存储为Class *用于调用方法。 当然,这很烂,因为它将使Data结构的大小增加一倍。

因此,如果有人想确认这一点(我希望被证明是错误的),我会很乐意将他们的答案标记为正确的……

每个指针都是不同的类型,但是它们都继承自一个通用的基类---我们称之为Base

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

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

在一个好的设计中,您实际上并不需要知道每个对象的类型,如果将其抽象化,则实际上会更好。 记住,针对接口而不是具体的类进行编程总是好的。

并将其投放到Base *可能涉及更改它们

并非如此,对于public继承,演员表应该是隐式的。

如果只对它们全部调用Foo() ,则Foo()可以是基类中的virtual方法,这样您就可以充分利用多态性。

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

尽管这是一个坏主意,但事实并非如此:您可以像在C语言中那样使用指针算法对指针进行迭代。指针的大小均相同,并且它们具有相同的基类(不考虑结构对齐问题等)。 它很脏,但在技术上却可以。 如果您将实例本身作为类的成员而不是指向它们的指针,那将是不同的。

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

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

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

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

您还可以按照以下可能的重复问题中的建议将数据强制转换为元组: 遍历struct变量

我不确定哪个更快...

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

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

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

我不想更改以前的答案。 但是,我提出了另一种适合您的解决方案:此处的完整示例: 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个实例...

另外,请用从类中检索N指针的东西替换FooSingleCall,例如GetNPointer ...

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

void foo( Base const* );

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

就这么简单。

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

生成vtable的时候确实有这种类型的东西(每个指针指向通常具有不同签名的函数),但这是非常罕见的。 因此,这听起来像是XY问题。 例如,您正在尝试解决问题X,提出了一个不好的解决方案Y,现在又在询问想象中的解决方案Y,而不是真正的问题X…

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM