Почему производный класс не будет работать в массиве?(C++)
-
03-07-2019 - |
Вопрос
Я создал класс, называемый 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, а не с его копией, если только вы сначала не создали копию и не инициализировали массив указателем на копию).