0

我有一个多个类继承的接口。

    class someInterface
    {
        virtual void someMethod() = 0;
    }
    class A : public someInterface
    {
        public:
            void someMethod()
            {
                //Do something
            }
    }

    class B : public someInterface
    {
        public:
            void someMethod()
            {
                //Do something
            }
    }

    class C : public someInterface
    {
        public:
            void someMethod()
            {
                //Do something
            }
    }

对于 A、B、C 类中的每一个,我在容器类中创建了一个具有不同大小的实际类型的数组。

    class AContainer 
    {
        public:
            A As[10];
    }

    class BContainer 
    {
        public:
            B Bs[5];
    }
    etc...

此外,我有一个指向“SomeInterface”的指针数组,我想在其中有一个指向每个像这样的实际数组的指针。

    #define SOMEINTERRFACE_SIZE 3
    someInterface *array[SOMEINTERRFACE_SIZE];
    array[0] = AContainer.As; //Could also just be &AContainer.As[0]
    array[1] = BContainer.Bs;
    array[2] = CContainer.Cs;

    for (int i = 0; i < SOMEINTERRFACE_SIZE; ++i)
    {
        int elements = //Here i need a solution to get the size
                       //So i can iterate through the array, which the pointer points to.
        for (int i = 0; i < elements; ++i)
        {
            //Call the interface method on each element.
        }
    }

当我必须使用 someInterface 数组时,就会出现问题,因为无法通过 someInterface 指针获取实际数组的大小。

这个问题有什么好的解决方案?我真的需要一些帮助来解决这个问题。也不想使用动态分配,所以没有使用 vector<> 或 malloc 等的解决方案,因为我正在写信给 Arduino。

4

3 回答 3

1

它行不通。在 C++ 中,您必须知道数组中元素的大小。 A, B, 并且C可能是不同的大小,因此您不能将它们的数组视为相同。

&AContainer.As[i] == &AContainer.As + i * sizeof(A)

&BContainer.Bs[i] == &BContainer.Bs + i * sizeof(B)

因此,相同的机器代码不可能遍历 ofA和 of 的数组B。如果要遍历对象数组,则需要知道确切的类型。

请记住,在 C++ 中,如果要获得多态虚拟调用,则需要通过指针或引用。解决方案是将指向每个数组中元素的指针复制到一个“主”数组中。

SomeInterface *ptrs[NUM_A + NUM_B + NUM_C];
SomeInterface **dest = ptrs;
for (int i = 0; i < NUM_A; ++i) {
    *dest++ = &AContainer.As[i];
}
for (int i = 0; i < NUM_B; ++i) {
    *dest++ = &BContainer.Bs[i];
}
// et cetera...

这只使用了一点额外空间,因为您存储的是指针,而不是实际的对象。

编辑:如果你真的想节省空间,我想你可以做这样的事情:

someInterface *arrays[] = { AContainer.As, BContainer.Bs, CContainer.Cs };
int objSizes[] = { sizeof(A), sizeof(B), sizeof(C) };
int arrLengths[] = { NUM_A, NUM_B, NUM_C };

for (int j = 0; j < sizeof(arrays)/sizeof(arrays[0]); ++j)
{
    void *ptr = arrays[j];
    for (int i = 0; i < arrLengths[j]; ++i) {
        someInterface *iptr = (someInterface *)ptr;
        iptr->method();
        ptr += objSizes[j];
    }
}

(这是未经测试的,您可能需要稍微调整一下。)

理论上,由于所有这些数组都充满了编译时常量,因此它应该快速优化。如果不这样做,代码将运行得更慢,因为它将通过仅在运行时而不是编译时知道的值递增指针。如果你真的关心速度,你应该检查汇编输出。

于 2014-11-19T16:39:22.203 回答
0

在不了解您的应用程序的更多细节的情况下很难回答 - 但这里有一些可能会有所帮助的想法。

鉴于:

class someInterface { public: virtual char someMethod() = 0; };

class A : public someInterface { public: char someMethod() { return 'A'; } };
class B : public someInterface { public: char someMethod() { return 'B'; } };
class C : public someInterface { public: char someMethod() { return 'C'; } };

你可以像这样手动滚动:

class Array {
public:
    void forEach( void(*function)(someInterface&) ) {
        for (size_t i = 0  ;  i < countA  ;  ++i) function(As[i]);
        for (size_t i = 0  ;  i < countB  ;  ++i) function(Bs[i]);
        for (size_t i = 0  ;  i < countC  ;  ++i) function(Cs[i]);
    }
private:
    enum {countA = 10, countB = 5, countC = 3};
    A As[countA];
    B Bs[countB];
    C Cs[countC];
};

void doSomeMethod(someInterface& element) {
    std::cout << element.someMethod();
}

int main(int, char**) {
    Array array;
    array.forEach(doSomeMethod);
    return 0;
}

请注意,通过使用“回调”函数doSomeMethod,我们解决了在多态集合中调度的典型问题。当然,您不想继续手动滚动这样的事情。幸运的是,我签出的 Arduino C++ 编译器支持模板,因此您可以执行以下操作:

template <class T, size_t _size, class NextArray = void>
struct Array {
public:
    typedef T value_type;
    enum {size = _size};

    void forEach( void(*function)(someInterface&) ) {
        for (size_t i = 0  ;  i < _size  ;  ++i)
            function(elements[i]);
        nextArray.forEach(function);
    }
private:
    T elements[_size];
    NextArray nextArray;
};

template <class T, size_t _size>
struct Array<T, _size, void> {
public:
    typedef T value_type;
    enum {size = _size};

    void forEach( void(*function)(someInterface&) ) {
        for (size_t i = 0  ;  i < _size  ;  ++i)
            function(elements[i]);
    }
private:
    T elements[_size];
};

void doSomeMethod(someInterface& element) {
    std::cout << element.someMethod();
}

int main(int, char**) {
    Array<A, 10, Array<B, 5, Array<C, 3> > > array;
    array.forEach(doSomeMethod);
    return 0;
}

这让编译器为您编写不同类型和大小的组合。有几点值得注意:

  1. 所有的魔法都是在编译时完成的。查看优化编译器生成的程序集,看看它有多小和多快。
  2. 如果您的回调函数需要一些状态,请阅读 c++“函子”。
  3. 如果您的编译器支持可变参数模板和/或 lambda,这会变得更简单(我假设 Arduido 编译器还没有)

如果您不能使用回调方法(并且您的编译器还不支持 lambdas),那么您可以尝试下一个选项,它会比上面给出的选项产生一些小的运行时成本:

template <class Interface>
class ArrayInterface {
public:
    virtual size_t getSize() = 0;
    virtual Interface& getElement(size_t index) = 0;
};

template <class T, class Interface, size_t size>
class Array : public ArrayInterface<Interface> {
public:
    size_t getSize() { return size; }
    Interface& getElement(size_t index) { return element[index]; }
private:
    T element[size];
};

int main(int, char**) {
    Array<A, SomeInterface, 10> As;
    Array<B, SomeInterface, 5> Bs;
    Array<C, SomeInterface, 3> Cs;

    const int SOMEINTERRFACE_SIZE = 3;
    ArrayInterface<SomeInterface>* array[SOMEINTERRFACE_SIZE] = {&As, &Bs, &Cs};

    for (size_t i = 0  ;  i < SOMEINTERRFACE_SIZE  ;  ++i) {
        ArrayInterface<SomeInterface>& innerArray = *array[i];
        for (size_t j = 0  ;  j < innerArray.getSize()  ;  ++j)
            std::cout << innerArray.getElement(j).someMethod();
    }

    return 0;
}

(根据您的问题,最后一个使用外部指针数组)

这篇文章已经太长了,所以我没有详细介绍,也没有深入研究诸如成员函数指针的单个平面数组之类的选项。如果您有任何问题,请大声喊叫。

于 2014-11-20T11:35:12.530 回答
-1

这是你想要达到的目标吗?遍历对象列表并调用公共接口的重新实现方法?

将这段代码放在全局 C++ 范围内的任何位置进行测试。

#include <vector>
#include <iostream>
int TestSomewhereInCppGlobalScopeCode()
{
    class someInterface
    {
        public:
            virtual void someMethod() = 0;
    };

    class A : public someInterface
    {
        public:
            void someMethod()
            {
                std::cout << "A::someMethod()";
            }
    };

    class B : public someInterface
    {
        public:
            void someMethod()
            {
                std::cout << "B::someMethod()";
            }
    };

    class C : public someInterface
    {
        public:
            void someMethod()
            {
                std::cout << "C::someMethod()";
            }
    };

    std::vector<someInterface*> ListOfObjectsHavingCommonInterface;

    ListOfObjectsHavingCommonInterface.push_back( new A );
    ListOfObjectsHavingCommonInterface.push_back( new B );
    ListOfObjectsHavingCommonInterface.push_back( new C );

    for ( std::vector<someInterface*>::iterator it = ListOfObjectsHavingCommonInterface.begin();
          it != ListOfObjectsHavingCommonInterface.end();
          ++it )
    {
        (*it)->someMethod();
    }

    return 0;
}

static int TestSomewhereInCppGlobalScopeCode_Caller = TestSomewhereInCppGlobalScopeCode();
于 2014-11-19T17:17:40.573 回答