我有一个包含许多(如数百个)指针的结构。每个指针都是不同的类型,但它们都从一个公共基类继承 - 让我们称它为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 *用于调用方法。当然,这很糟糕,因为它会使数据结构的大小翻倍。
所以,如果有人想证实这一点(我希望被证明是错误的),我很乐意将他们的答案标记为正确......
答案 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;
答案 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 ...