指向数组的指针维护元素的计数器

时间:2014-11-19 16:11:13

标签: c++ static-allocation

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

    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指针获取实际数组的大小。

这个问题的解决方案是什么?我真的需要一些帮助来解决这个问题。 也不想使用动态分配,因此没有带矢量的解决方案&lt;&gt;或者malloc等因为我写信给Arduino。

3 个答案:

答案 0 :(得分:1)

它不会起作用。在C ++中,您必须知道数组中元素的大小。 ABC的大小可能不同,因此您无法对它们的数组进行相同的处理。

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

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

因此,相同的机器代码无法迭代AB的数组。如果要迭代一组对象,则需要知道确切的类型。

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

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

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

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

答案 1 :(得分: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 ++“functors”。
  3. 如果您的编译器支持可变参数模板和/或lambdas,这会变得更简单(我假设Arduido编译器还没有)
  4. 如果你不能使用回调方法(并且你的编译器还不支持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;
    }
    

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

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

答案 2 :(得分:-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();