Форум программистов
 
Контакты: о проблемах с регистрацией, почтой и по другим вопросам пишите сюда - alarforum@yandex.ru, проверяйте папку спам! Обязательно пройдите активизацию e-mail.

Вернуться   Форум программистов > C++ > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Здесь нужно купить рекламу за 25 тыс руб в месяц! ) пишите сюда - alarforum@yandex.ru
Без учёта ботов - 20000 человек в день, 350000 в месяц.

Ответ
 
Опции темы
Старый 17.05.2013, 00:41   #1
actac
 
Регистрация: 17.05.2013
Сообщений: 3
По умолчанию наследование и указатели

вопрос наверное очень простой, но не могу понять
Код:
class Base
{
public:
    Base() {}
    virtual void print(){ cout << "base class\n"; }
}

class Derived : public Base
{
public:
    Derived() : value(5) {}
    void print() { cout << "derived class - value = "<< value ; }
    int value;
}

int main()
{
    Base* arrayOfDerived = new Derived[2];
    arrayOfDerived[0].print(); // uncaught exception - it's OK
    arrayOfDerived[1].print(); // previous line was commented - 5 expected but uncaught exception
}
Как вижу последовательность действий я:
1. Создаётся первый объект Derived, размещается в куче
2. Создаётся второй объект Derived, размещается в куче через n байтов, где n - размер класса Base.
3. из-за такого размещения теряется информация о значении value для первого объекта Derived, для второго всё в порядке

почему для второго объекта не выводится значение value? объясните пожалуйста, в чем ошибка в моих рассуждениях, или посоветуйте что почитать на эту тему
actac вне форума   Ответить с цитированием
Старый 17.05.2013, 07:55   #2
Stilet
Белик Виталий :)
Старожил
 
Аватар для Stilet
 
Регистрация: 23.07.2007
Сообщений: 57,840
По умолчанию

Цитата:
размещается в куче через n байтов, где n - размер класса Base.
Чего это так? Если я правильно понимаю, то создаются два указателя. Объекты еще не созданы. И создаваться объекты будут не рядом а именно по правилам кучи - где свободно.
Цитата:
Base* arrayOfDerived = new Derived[2];
А почему базовый берется? Там же виртуальная функция.
Я думаю должно быть так:
Код:
    Derived arrayOfDerived[2];
    for(int i=0;i<2;i++) arrayOfDerived[i] = new Derived();
    arrayOfDerived[0].print(); // uncaught exception - it's OK
    arrayOfDerived[1].print(); // previous line was commented - 5 expected but uncaught exception
I'm learning to live...
Stilet вне форума   Ответить с цитированием
Старый 17.05.2013, 15:01   #3
Igor95
Форумчанин
 
Регистрация: 03.01.2013
Сообщений: 388
По умолчанию

for(int i=0;i<2;i++) arrayOfDerived[i] = new Derived();

Ошибка. Вы не присваиваете память указателю, вы его разыменовываете и пытаетесь сделать так, что бы он указывал на память выделенную под Derived.
Igor95 вне форума   Ответить с цитированием
Старый 17.05.2013, 16:04   #4
Abstraction
Старожил
 
Аватар для Abstraction
 
Регистрация: 25.10.2011
Сообщений: 3,178
По умолчанию

Цитата:
1. Создаётся первый объект Derived, размещается в куче
2. Создаётся второй объект Derived, размещается в куче через n байтов, где n - размер класса Base.
Неправильно.
1) В куче выделяется место, достаточное для размещения двух объектов типа Derived.
2) Дважды вызывается конструктор Derived без параметров, инициализируя два элемента этого массива.
3) Указатель на первый элемент присваивается переменной arrayOfDerived.
4) При вызове arrayOfDerived[0].print() вычисляется arrayOfDerived[0], трактуется как объект типа Base и для него вызывается print(). Если не изменяет память, стандарт не гарантирует корректной работы такого кода (не уверен). В случае Visual Studio 2010, однако, он отрабатывает нормально и выводит 5 (т.е. успешно вызвалась Derived:: print(), и успешно обратилась к значению value).
5) При вызове arrayOfDerived[1].print(), однако, начинаются интересные вещи. Ведь компилятор высчитывает положение второго элемента массива как "начало массива плюс размер первого элемента". А размер в этом случае вычисляется статически, то есть смещение равно sizeof(Base). В итоге arrayOfDerived[1] начинается непонятно где, и поведение программы становится вполне неопределённым. Однако, в реальном случае, мы получим, что там, где ожидался указатель на таблицу виртуальных функций объекта, оказался "хвост" от Derived: значение 5. После чего программа попытается обратиться по адресу 0x00000005 с весьма предсказуемыми последствиями.
6) Однако даже если закомментировать эту строку, остаётся фундаментальная причина, по которой так писать нельзя: деструкторы. Когда будет вызван delete[] arrayOfDerived, компилятор сгенерирует код, который вызовет деструкторы для arrayOfDerived[0] и arrayOfDerived[1], ровно с той же проблемой при вычислении границ arrayOfDerived[1].
Abstraction вне форума   Ответить с цитированием
Старый 17.05.2013, 16:15   #5
Igor95
Форумчанин
 
Регистрация: 03.01.2013
Сообщений: 388
По умолчанию

4) А почему объект типа Base? Там ведь позднее связывание срабатывает. Тогда, будет вызван метод для объекта того типа на который указывает указатель типа Base...
или я чего-то недопонял...
Igor95 вне форума   Ответить с цитированием
Старый 17.05.2013, 16:21   #6
Abstraction
Старожил
 
Аватар для Abstraction
 
Регистрация: 25.10.2011
Сообщений: 3,178
По умолчанию

Цитата:
4) А почему объект типа Base? Там ведь позднее связывание срабатывает. Тогда, будет вызван метод для объекта того типа на который указывает указатель типа Base...
или я чего-то недопонял...
Позднее связывание позволило нам вызвать метод производного класса через указатель базового. Однако, если добавить в классы невиртуальный метод print_type(), то arrayOfDerived[0].print_type() вызовет Base :: print_type().

Р-р-р... третий пост, и третий смайлик подряд. Достало.
Abstraction вне форума   Ответить с цитированием
Старый 17.05.2013, 16:24   #7
Igor95
Форумчанин
 
Регистрация: 03.01.2013
Сообщений: 388
По умолчанию

Естественно, но почему Вы написали, что
Цитата:
трактуется как объект типа Base
?
Igor95 вне форума   Ответить с цитированием
Старый 17.05.2013, 16:37   #8
Abstraction
Старожил
 
Аватар для Abstraction
 
Регистрация: 25.10.2011
Сообщений: 3,178
По умолчанию

Цитата:
Естественно, но почему Вы написали, что
Цитата:
трактуется как объект типа Base
?
Потому что, в момент компиляции, arrayOfDerived[0] - это объект типа Base, и все вызовы для него компилируются как для объекта типа Base. Вызов виртуального метода для объекта типа Base использует таблицу виртуальных методов Base и, за счёт этого, может привести к вызову методов наследников на этапе исполнения, но не более того. В частности, даже если вы класс Derived содержал виртуальный метод unique(), его нельзя было бы вызвать через arrayOfDerived[0] без явного приведения типа (потому что этого метода нет в таблице виртуальных функций Base).
Abstraction вне форума   Ответить с цитированием
Старый 17.05.2013, 18:50   #9
waleri
Старожил
 
Регистрация: 13.07.2012
Сообщений: 5,970
По умолчанию

Цитата:
Сообщение от actac Посмотреть сообщение
2. Создаётся второй объект Derived, размещается в куче через n байтов, где n - размер класса Base.
Нет, n - это размер класса Derived. Отсюда и проблемы. Когда пытаетесь обратиться ко второму елементу ето получается
ptr + sizeof(Base) (а не Derived) в результате чего, указатель указывает кудато не туда.

В С/С++ нельзя делать массивы разных типов. Если масив Base, значит все елементы Base, если Derived, тогда Derived.

В данном случае надо делать массив из указателей:
Base* array[2];
array[0] = new Derive;
array[1] = new Derive;

Ну и сам массив можно выделять динамически, если надо.
waleri вне форума   Ответить с цитированием
Старый 17.05.2013, 20:05   #10
Stilet
Белик Виталий :)
Старожил
 
Аватар для Stilet
 
Регистрация: 23.07.2007
Сообщений: 57,840
По умолчанию

Цитата:
Вы не присваиваете память указателю
Согласен. Не досмотрел.
I'm learning to live...
Stilet вне форума   Ответить с цитированием
Ответ
Опции темы


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Наследование в С++ Theseus Общие вопросы C/C++ 10 21.07.2012 20:06
Наследование Настюнечка Помощь студентам 0 29.11.2011 18:40
наследование настюх Помощь студентам 8 27.11.2011 13:54
Наследование Сергей089 Помощь студентам 6 07.02.2010 13:04
[C] массивы, указатели, двойные указатели. Iggel Общие вопросы C/C++ 5 05.05.2009 12:39