Почему производный класс не будет работать в массиве?(C++)

StackOverflow https://stackoverflow.com/questions/816463

Вопрос

Я создал класс, называемый vir, с функцией move:

class vir
{
public:
     vir(int a,int b,char s){x=a;y=b;sym=s;}
     void move(){}
};

(Он производен от класса с переменными int x, int y и char sym) Я вывел из этого класс, называемый subvir:

class subvir:public vir
{
public:
     subvir(int a,int b,char s){x=a;y=b;sym=s;}
     void move();
};
subvir::move()
{
     x++;
     return;
}

А затем я создал массив vir и поместил в него subvir

subvir sv1(0,0,'Q');
vir vir_RA[1]={sv1};

Но когда я пытаюсь использовать sv1.move():

vir_RA[0].переместить();

Он использует перемещение vir ({}), а не перемещение subvir ({x++}).Я попытался сделать sv1 vir, а vir_RA - vir, и это работает, и это также работает, когда я создаю их оба subvir, но мне нужно, чтобы они были разными.Я попытался сделать vir::move() чисто виртуальным, но затем я получаю ошибку, подтверждающую массив.Кто-нибудь знает, как я могу заставить move() работать, когда я использую его из массива?

Это было полезно?

Решение

В этом случае вам нужен массив указателей, а не массив экземпляров.Используйте vir*[] вместо vir[]

Другие советы

Вы сталкиваетесь с проблемой, называемой нарезка.Используйте массив указателей или что-то вроде Boost.ptr_контейнер.

Базовый класс должен иметь virtual функции, которые дают вам то, что вы хотите, делая их чистыми, приведут к созданию абстрактного базового класса - чего-то, что вы не можете создать.Однако вы все еще можете создавать указатели / ссылки на абстрактные базовые классы и присваивать им объекты производного класса.Ваш базовый класс лучше всего представлен в виде:

class vir
{
public:
     vir(int a,int b,char s){x=a;y=b;sym=s;}
     virtual void move(){}
};

Это делает производный класс move в том числе и виртуальный.Однако ваш move в определении отсутствует возвращаемое значение, и оно не будет компилироваться.Попробуй:

void subvir::move()
{
     x++;
     return;
}

Обратите внимание, что для работы динамической привязки вам нужны либо указатели (как упоминалось в других ответах), либо ссылки на производные классы.Итак, вместо массива vir объекты, использующие массив указателей базового класса:

vir* v[ 2 ] = { new subvir(0, 0, 'Q'), new subvir(10, -10, 'P') };

Вам также следует f Обязательно ознакомьтесь со следующими разделами C ++ FAQ Lite:

Две вещи.Массив представляет собой массив vir, поэтому, конечно, он использует vir::move .move() - это не виртуальный метод.

Но более важным является нарезка.Вы не можете помещать подклассы в массив.Если sizeof vir != sizeof subvir, массив не будет выстроен правильно.В настоящее время они имеют одинаковый размер.Но что произойдет, если это не так?

Да, в основном компилятор не допускает подклассы в массивах, потому что массивы инициализируются строго для размера типа, а подтипы имеют тенденцию быть больше родительских, и это привело бы к проблемам, если бы вы могли инициализируйте массивы значениями подтипа.Что действительно происходит, так это то, что компилятор сначала выделяет массив размером N * байт (base_type).И затем он копирует байты размера (base_type) каждого из объектов инициализации .если бы они были разных типов, они были бы усечены, и в вашем коде могли бы происходить странные вещи.

Позвольте мне обобщить предыдущие ответы.

На самом деле здесь есть две проблемы.Один из них - нарезка.Вы инициализируете массив виртуальных файлов копией вложенного файла.В таких случаях компилятор вырезает часть vir из вложенного файла и копирует ее в массив, так что вы действительно получаете там только объекты vir.Теперь, в вашем конкретном случае, subvir не имеет дополнительных элементов данных, кроме элементов vir, поэтому нарезка несколько вырождена, и объект vir очень похож на объект subvir.Однако vir и subvir - это разные классы, и объект в массиве в конечном итоге является объектом vir, а не объектом subvir, замаскированным под vir.Один из способов, которым разница между ними проявилась бы практически, даже если бы оба имели одинаковые элементы данных, заключается в том, что у vir были виртуальные функции, перегруженные subvir.В этом случае указатель vtable на объект в массиве будет указывать на vtable vir, а не на subvir.Конечно, это было бы еще более явно, если бы subvir содержал дополнительные элементы данных, не найденные в vir.

Вторая проблема - это полиморфизм.В момент использования (вызов функции move()) компилятор думает, что вы вызываете метод move() объекта типа vir (поскольку массив представляет собой массив virs).(Компилятор, конечно, прав, думая так из-за нарезки, какой бы вырожденной она ни была в данном случае.) Если бы это действительно был объект subvir, как вы намеревались, вы могли бы вызвать subvir::move(), сделав move() виртуальным в vir.

Чтобы получить желаемое поведение, вы могли бы использовать массив указателей (но тогда вы работали бы непосредственно с sv1, а не с его копией, если только вы сначала не создали копию и не инициализировали массив указателем на копию).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top