0

我有一个包含很多(比如数百个)指针的结构。每个指针都是不同的类型,但它们都继承自一个公共基类 --- 我们称它为 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* 数组中进行迭代,一次作为 ClassWhatever * 用于调用方法。当然,这很糟糕,因为它会使数据结构的大小增加一倍。

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

4

6 回答 6

4

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

与其拥有数百个成员,不如保留一个Base类指针容器:

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

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

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

不是真的,对于public继承,演员表应该是隐式的。

如果你只调用Foo()它们,Foo()可以是virtual基类中的一个方法,这样你就可以充分利用多态性。

于 2012-07-08T18:26:50.137 回答
0

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

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

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

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

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

于 2012-07-08T19:10:55.860 回答
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 ...

于 2012-07-08T20:14:38.933 回答
0

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

您还可以尝试按照此可能重复的问题中的建议将您的 Data 转换为元组:Iterate through struct variables

我不确定哪个更快...

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

于 2012-07-08T19:38:05.177 回答
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;
};
于 2012-07-08T19:38:11.133 回答
-1

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

void foo( Base const* );

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

就这么容易。

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

在生成 vtables(每个指针指向一个通常具有不同签名的函数)时,确实会得到这种东西,但这种情况非常罕见。所以,这听起来像是一个 XY 问题。就像,你试图解决问题 X,想出了一个不好的解决方案 Y,现在正在询问想象的解决方案 Y,而不是真正的问题 X……

于 2012-07-08T19:33:15.913 回答