Как вернуть указатель на массив из функции, используя структуру как интерфейс

Я пытаюсь создать интерфейс для массива в C++.

В приведенном ниже коде tmpClass[1].GetA() возвращает 'w' tmpInterface[1] вызывает ошибку. Есть ли способ определить интерфейс, чтобы он мог получить доступ к элементам массива? Как заставить tmpInterface вести себя как tmpClass?

struct IA
{
 virtual char GetA() = 0;
 virtual void SetA(char pA) = 0;
};

class A:public IA
{
 public:
 A(){ var = 0; }
 A(char pVar){ var = pVar; }
 char GetA(){ return var; }
 void SetA(char pA){ var = pA; }
 private:
 int var;
};

class B
{
 public:
 B(){ 
 mA[0].SetA('c');
 mA[1].SetA('w');
 mA[2].SetA('6');
 mA[3].SetA('$');
 }

 int GetCount(){}
 IA* Get1(){ return mA; }
 A* Get2(){ return mA; }
 protected:
 A mA[4];
};


int main()
{
 B mainClass;
 IA *tmpInterface = mainClass.Get1();
 A *tmpClass = mainClass.Get2();

 for (int i = 0; i < 4; i++)
 {
 //once i>0 then tmpInterface no longer points to a valid character
 //and program crashes
 System::Console::Write(
 "A = "+tmpClass[i].GetA()+
 " IA = "+tmpInterface[i].GetA()); 
 }
 return 0;
}
4 ответа

Оператор индекса использует арифметику указателя и выполняет арифметику указателя на указателе, тип которого отличается от фактического типа объекта, на который он указывает, является неопределенным поведением. §5.7 [expr.add]/p7:

Для сложения или вычитания, если выражения P или Q имеют тип "указатель на cv T ", где T отличается от типа элемента с неквалифицированным массивом, поведение не определено. [Примечание. В частности, указатель на базовый класс не может использоваться для арифметики указателя, когда массив содержит объекты производного типа класса. -End note]

Итак, чтобы индексировать массив A s, вам нужен A*. Если вы хотите использовать только IA, тогда вам нужно применить некоторую программу-панацею - косвенность. Верните IA ** указывающий на первый элемент массива IA *, члены которого каждый указывают на A


В эффективном C++, он говорит, что не используйте полиморфизм в массиве; Вы можете добавить, что виртуальная функция ведет себя как operator [], например:

struct IA
{
 //...
 virtual IA* Offset(int index)
 {
 return this + index;
 }
};

class A:public IA
{
public:
 //...
 virtual A* Offset(int index)
 {
 return this + index;
 }
};

то он отлично работает.

cout << "A = " << tmpClass->Offset(i)->GetA();
cout << " IA = " << tmpInterface->Offset(i)->GetA() << endl;

Кроме того, что такое System :: Console :: Write в C++?


В объекте типа B вас есть массив объектов A Вы возвращаете указатель на первый элемент массива как в функциях-членах B::Get1() и в B::Get2().

Скажем, выложим массив:

+--------------+--------------+--------------+--------------+
| mA[0] | mA[1] | mA[2] | mA[3] | 
+--------------+--------------+--------------+--------------+

Когда вы выполняете

IA *tmpInterface = mainClass.Get1();
A *tmpClass = mainClass.Get2();

У вас есть tempInterface и tmpClass указывающие на первый элемент mA.

+--------------+--------------+--------------+--------------+
| mA[0] | mA[1] | mA[2] | mA[3] | 
+--------------+--------------+--------------+--------------+
^
| 
tmpInterface as well as tmpClass

Когда вы выполняете арифметические операции на tmpInterface и tmpClass, вы увидите очень разные результаты.

Где tmpInterface+1 и tmpClass+1 точка?

+--------------+--------------+--------------+--------------+
| mA[0] | mA[1] | mA[2] | mA[3] | 
+--------------+--------------+--------------+--------------+
 ^ ^
 | | 
 | tmpClass+1
 tmpInterface+1

Поскольку tmpClass имеет тип A*, tmpClass+1 указывает на следующий объект в массиве. Однако, поскольку тип tmpInterface равен IA*, а sizeof(IA) не совпадает с sizeof(A), tmpInterface+1 указывает на что-то посередине. Он не указывает на объект типа IA. Если вы попытаетесь получить доступ к tmpInterface+1 как IA*, вы получите неопределенное поведение.


Обратите внимание, что полиморфизм работает только с указателями или ссылками на экземпляры класса, а не с экземплярами напрямую. Это лежит в основе вашей проблемы.

Вы можете использовать std::vector вместо массива.

licensed under cc by-sa 3.0 with attribution.