массив производных классов не работает, если производный класс имеет закрытые переменные - PullRequest
0 голосов
/ 08 июня 2011

У меня есть абстрактный базовый класс и 2 разных класса, которые реализуют виртуальную функцию из базового класса.

Я поместил их в массив, и для класса «output1» это работает.Однако, если я создам массив класса «производный2», в котором есть некоторые дополнительные частные переменные, код будет скомпилирован, но с ошибками во время выполнения.массив?

Спасибо

edit:

Код segfaults, и valgrind сообщает мне

-2

==25096== Use of uninitialised value of size 8
==25096==    at 0x400AC9: main (abc.cpp:52)
==25096== 
==25096== Invalid read of size 8
==25096==    at 0x400AC9: main (abc.cpp:52)
==25096==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==25096== 
==25096== 
==25096== Process terminating with default action of signal 11 (SIGSEGV)
==25096==  Access not within mapped region at address 0x0
==25096==    at 0x400AC9: main (abc.cpp:52)

Ответы [ 6 ]

7 голосов
/ 08 июня 2011

массив из derived1 объектов (или derived2 объектов) не может быть интерпретирован как массив из base объектов.Массивы не являются полиморфными.Только автономный объект может быть полиморфным, то есть derived1 имеет отношение IS-A с base.Но array of derived1 не имеет отношения IS-A с array of base.Ни один из ваших массивов (ни classAry1, ни classAry2) действительно не может "работать".

Другими словами, этот

base *classAry1 = new derived1[3];
base *classAry2 = new derived2[2];

уже не имеет никакого смысла, даже если это формально правильно сформированный код.

Первый массив "работает"просто случайно.Поведение вашего кода не определено, даже когда вы работаете с classAry1.

Если вы хотите иметь массив (или контейнер), в котором хранятся полиморфные сущности, вы должны хранить указатели для реальных объектов в этом массиве, вместо хранения самих реальных объектов.

В вашем конкретном случае код можно переписать следующим образом.(Это выглядит не очень хорошо, и я просто делаю это, чтобы проиллюстрировать принцип, поскольку, не зная вашего полного намерения, трудно выбрать лучший подход)

int main(){
  derived1 *d1s = new derived1[2];
  base **classAry1 = new base *[2];
  classAry1[0] = &d1s[0];
  classAry1[1] = &d1s[1];

  derived2 *d2s = new derived2[2];
  base **classAry2 = new base *[2];
  classAry2[0] = &d2s[0];
  classAry2[1] = &d2s[1];

  classAry1[0]->setInner(1,3);
  classAry1[1]->setInner(10,7);
  std::cout << classAry1[0]->doStuff() << std::endl;;
  std::cout << classAry1[1]->doStuff() << std::endl;

  classAry2[0]->setInner(1,3);
  classAry2[1]->setInner(10,7);
  std::cout << classAry2[0]->doStuff() << std::endl;;
  std::cout << classAry2[1]->doStuff() << std::endl;

  delete[] classAry2;
  delete[] d2s;
  delete[] classAry1;
  delete[] d1s;

  return 0;
}
1 голос
/ 08 июня 2011

classAry2 [1] => & classAry2 + sizeof (base), который не равен фактическому производному размеру, равному> sizeof (base) или производному1.Вам нужно получить правильный адрес (2-й элемент) для classAry2, который является classAry2 + sizeof (производный 2).

Самый простой обходной путь - это получить к ним доступ, используя массив указателей на базу *.Используйте base ** = выделите массив base * Затем присвойте каждому элементу правильный экземпляр (производный или производный2 или базовый). Таким образом, когда вы используете classAry [n] или classAry ++, он всегда будет указывать на правильный адрес.

1 голос
/ 08 июня 2011

В общем, вы не можете делать то, что пытаетесь сделать. Вы вызываете неопределенное поведение . Когда система сталкивается с неопределенным поведением, она может делать все, что захочет (включая стирание жесткого диска), и при этом соответствовать стандартам. Проблема в вашем коде.

0 голосов
/ 22 марта 2012

читать это: http://www.parashift.com/c++-faq/proper-inheritance.html

Чрезвычайно хорошо описаны, и это оставляет вас с чувством просветления:)

0 голосов
/ 08 июня 2011

Вы не можете этого сделать. Массив одного типа T1 не может быть преобразован в массив другого типа T2, даже если один является производным от другого.

На твоей линии

  base *classAry1 = new derived1[3];//this works

На самом деле у вас есть указатель базового класса для первого элемента массива, только . Другие элементы "потеряны". Доступ к членам массива, отличным от первого, с использованием указателя базового класса, является неопределенным поведением.

0 голосов
/ 08 июня 2011

Попробуйте использовать std :: vector вместо необработанного массива.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...