У меня есть интерфейс, который наследует несколько классов.
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.
Какое хорошее решение этой проблемы? Мне действительно нужна помощь, чтобы решить это.
Также не хочу использовать динамическое распределение, поэтому нет решения с вектором<> или malloc и т. д. потому что я пишу в Arduino.
Это не сработает. В C ++ вы должны знать размер элементов в массиве. A
, B
, а также C
могут быть разных размеров, поэтому вы не можете обращаться с их массивами одинаково.
&AContainer.As[i] == &AContainer.As + i * sizeof(A)
но
&BContainer.Bs[i] == &BContainer.Bs + i * sizeof(B)
Так что для одного и того же машинного кода невозможно перебирать массивы A
и из 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];
}
}
(это не проверено, возможно, вам придется немного подправить.)
Теоретически, поскольку все эти массивы полны констант времени компиляции, он должен оптимизироваться до чего-то быстрого. Если этого не произойдет, код будет работать медленнее, потому что он будет увеличивать указатели на значение, известное только во время выполнения, а не во время компиляции. Вы должны проверить вывод сборки, если вы действительно заботитесь о скорости.
Трудно ответить, не зная более подробной информации о вашем приложении — но вот несколько идей, которые могут помочь.
Дано:
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;
}
Который заставляет компилятор написать это для вас для различных комбинаций типов и размеров. Несколько вещей, на которые стоит обратить внимание:
Если вы не можете использовать метод обратного вызова (и ваш компилятор еще не поддерживает лямбда-выражения), вы можете попробовать следующую опцию, которая требует небольших затрат времени выполнения по сравнению с опцией, указанной выше:
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;
}
(Этот последний использует внешний массив указатели, согласно вашему вопросу)
Этот пост уже слишком длинный, поэтому я не вдавался в подробности и не углублялся в такие опции, как единый плоский массив указателей на функции-члены. Если у вас есть какие-либо вопросы, просто кричите.
Это то, что вы пытаетесь достичь?
Перебирать список объектов и вызывать переопределенный метод общего интерфейса?
Поместите этот кусок кода в любую область глобальной 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();