пример наследования не выводит ожидаемые результаты

0

Попытка запомнить основной материал C++ (это было долгое время) и играть с компилятором. Я создал простой пример базового/дочернего наследования.

Я ожидал, что ниже выйдет

index 0 is 0
index 1 is 1
index 2 is 2

но вместо этого получите:

index 0 is 0
index 1 is 2
index 2 is 0

Может кто-то указать, что, очевидно, является плохой ошибкой с моей стороны?

#include <cstdlib>
#include <iostream>

#include <stdio.h>
#include <string>
using namespace std;

class Base
{
public: 
    Base(){x=0;}
    int x;
};
class Derived : public Base
{
public:
    Derived() { y=0;}
    int y;
};

// practicing operator definition syntax
ostream& operator<<(ostream& ostr, const Base& base)
{
       ostr << base.x << endl;
       ostr << flush;
    return ostr;
}

void init(Base *b)
{
    for (int i = 0; i<3; i++)
    {
        b[i].x=i; 
    }
};

int main(int argc, char** argv)
{
    Derived arr[3];
    init(arr);
    for (int idx = 0; idx< 3; idx++)
    {
        cout << "index is " << idx << ' ' << arr[idx] << endl;
    }

    return 0;
}
  • 0
    Что вы передаете в init ?
  • 0
    видимо, неправильная вещь (см. ответы ниже)
Теги:
inheritance

2 ответа

5
Лучший ответ

Массивы и полиморфизм не смешиваются в C++.

Derived и Base объекты имеют разные размеры, любая арифметика указателя, участвующая в вашей программе, терпит неудачу.

Ваш метод init нарезает Derived объекты в Base объектах. Следующее присваивание имеет неопределенное поведение, оно устанавливает некоторые байты где-то на объекте Derived.

Рассмотрите возможность использования std::vector<std::unique_ptr<B>> в качестве замены.

Кроме того, в вашем Base классе отсутствует его виртуальный деструктор, который вызывает более неопределенное поведение позже.

  • 1
    +1 хотя отсутствие виртуального деструктора здесь не проблема.
  • 0
    «Массивы и полиморфизм не смешиваются ...». Я не знал об этом. Я подумал, так как массив является просто указателем на первый объект в массиве, это было бы похоже на следующее: Base * b = new Derived [3]; Ах .... да, вы все правы. Спасибо за пробуждение давно умерших клеток мозга! :-)
4

Массив для производного типа не является массивом базового типа! Хотя указатель на производные объекты преобразуется в указатель на базовый объект, вы не можете использовать базовый указатель в качестве указателя в массив базовых объектов.

Обоснование довольно просто: когда вы выполняете операцию типа array[i] компилятор переводит это значение в *(array + i) а внутренняя арифметика адресов выполняется как нечто вроде array + sizeof(T) * i где T - статический тип array. Теперь, для типа D полученного из базового типа B он обычно имеет sizeof(B) < sizeof(D). В результате, если вы обрабатываете массив производных объектов как массив основанных объектов, то арифметика индекса приведет к доступу элементов в более или менее случайных местах объекта.

  • 0
    Да, на месте Спасибо.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню