Консультация № 55843
18.09.2006, 14:46
0.00 руб.
0 11 1
Доброго времени суток. Я столкнулся с одной задачей, которая совершенно сбивает с толку.
В задаче необходимо реализовать связный список объектов классов, наследуемых от одного абстрактного класса. А именно, написать 2 функции : добавления нового элемента списка и просмотра уже добавленных. По условию задачи эти функции должны быть чисто виртуальными и переопределёнными во всех классах потомках. Указатель на начало списка - статический указатель на базовый класс.
В процессе решения я написал код (см приложение), который по идее должен работать для всех классов потомков(т.е. виртуальность не нужна). Однако в задаче говорится именно о невозвожности правильного решения без использования виртуальности.

Приложение:
class Place { public: Place(); virtual ~Place(); virtual Place *Add()=0; //virtual??? virtual Place *show()=0; //virtual???protected: static Place *begin; Place *next;};class Area: public Place {public: Area(); ~Area(); Place *Add(); Place *show();};class Town: public Place {public: Town(); ~Town(); Place *Add(); Place *show();};//это было условие задачи. Однако можно-ли так : //Предположим, следующая ф-ция вызывается так// Place *obj = new Town;// obj->Add();Place Place::Add() { if (tail) tail->next=this; //tail - конец списка tail=this; this->next=NULL if(!begin) begin=tail;}

Обсуждение

Неизвестный
18.09.2006, 15:04
общий
это ответ
Здравствуйте, Satas!
Правильно ругается. Абстрактные методы класса не могут быть реализованы в самом абстрактном классе, а реализуются только в его потомках. См. Приложение

Приложение:
class Place { public: Place(); virtual ~Place(); virtual Place *Add()=0; //virtual??? virtual Place *show()=0; //virtual???protected: static Place *begin; Place *next;};class Area: public Place {public: Area(); ~Area(); Place *Add(); Place *show();};class Town: public Place {public: Town(); ~Town(); Place *Add(); Place *show();};//это было условие задачи. Однако можно-ли так : //Предположим, следующая ф-ция вызывается так// Place *obj = new Town;// obj->Add();Place Area::Add()/* или Twon:Add()*/ { if (tail) tail->next=this; //tail - конец списка tail=this; this->next=NULL if(!begin) begin=tail;}
Неизвестный
18.09.2006, 15:33
общий
Как работают виртуальные ф-ции и где их инициализировать я - знаю. Вопрос был не в этом , а в том, можно - ли обойтись ->без<- виртуальности. Естественно, ключевое слово virtual надо убрать, как собственно и прототипы ф-ций из классов-потомков.
Неизвестный
18.09.2006, 16:27
общий
Без виртуальности у вас будет просто переопределение методов в наследниках. При этом достаточно просто перепутать сигнатуру (список параметров) вызова. Поскольку у вас массив объектов базового класса, то очень даже может быть, что для всех объектов вызовется бзаовый метод. А если он будет виртуальный, то это вам гарантирует то, что базовый метод не вызовется.Мог сглючить.
Неизвестный
19.09.2006, 10:31
общий
Хорошо. Однако давайте оттолкнёмся от теории (всё то, что мне здесь писали - чисто книжный материал). Допустим, я переопределяю фиртуальную ф-цию Add() в классе Area() :Place *Area::Add() { if (tail) tail->next=this; //tail - конец списка tail=this; this->next=NULL if(!begin) begin=tail;}Это, насколько я могу судить, неправильный вариант. Тов. Кирилл Владимирович утверждает, что список параметров, передаваемых ф-циям, должен БЫТЬ. Но какие это должны быть параметры? Что вообще должна делать эта ф-ция кроме того, что она уже делает?Заранее благодарю.
Неизвестный
19.09.2006, 11:19
общий
Список параметров может быть пустым. И тогда тоже считается, что он есть. Только пустой. Впрочем, не в нем дело.Хороший пример есть в "Thinking in Java" ("Философия Java") - абстрактный класс shape, а от него уже наследуются конкретные: round, square, star... Введение виртуального метода обязывает всех, кто будет использовать некий базовый класс для наследования (в т.ч. и вас смаих), определять в нем некий минимальный набор функциональности. Но не только требует, но и, соотв., гарантирует реализацию этой минимальной функциональности в любом наследнике класса с виртуальными функциями.
Неизвестный
19.09.2006, 13:44
общий
И всё же. Данной реализации ф-ции Add() абсолютно безразлично какой объект её вызывает. Смысла в тупом копировании одного и того же кода я не вижу. Ещё раз повторюсь - именно в такой реализации. При следующих вызовах будет выполняться один и тот же код://-----------------------------------Place *obj1 = new Area;Place *obj2 = new Town;obj1->Add();obj2->Add();//-----------------------------------Тут либо я глупый, либо не учтены какие-то детали реализации. P.S. Уфффф.
Неизвестный
19.09.2006, 17:50
общий
Читайте <a href=http://ru.wikipedia.org/wiki/Виртуальная%20функция>здесь</a> о различиях виртуальных и невиртуальных функций.В том коде, который у вас есть, никакого различия не будет.Получая некий элемент списка, вы заранее не знаете, какого он типа, поэтому объект (указатель) вы сохраняете в переменную базового типа Place.Если ф-ия в базовом классе виртуальная, то произойдет определение типа во время исполнения (RTTI) и вызовется функция объекта именно того типа, которого на самом деле является данный объект. А если невиртуальная, то вызовется Place->метод, несмотря на то, что вы сохраняли только объекты типов Town и Area. Add для всех типов одинаковая, а вот Show - нет. Допишите свой код, а в Show вставьте что-дь простое типа printf("I‘m a Town") и printf("I‘m an Area"). И попробуйте оба типа функций для базового класса - виртуальные и невиртуальные.Тем не менее, Add объявить в базовом классе все равно придется. Если ее не объявлять вообще, то код из последнего вашего ответа на строкеobj1->Add();заглючит.Где тут "тупое кодирование", если вам надо только заголовок ф-ии определить в базовом классе?
Неизвестный
20.09.2006, 10:26
общий
Ок. Кажется разобрался. Резюме : ф-ция Show() обязательно должна быть виртуальной. Add()- необязательно. Её, например, можно поместить в конструктор базового класса и вызывать через конструктор производного класса (например, передать сообщение "object Area/Town has been added.") ://------------------------------Place() { if (tail) tail->next=this; tail=this; this->next=NULL; if(!begin) begin=tail;}//------------------------------Спасибо за популярное объяснение!
Неизвестный
20.09.2006, 12:26
общий
Гм. Я опять в затруднении. Пока дописывал программу, пришла в голову такая мысль : объявить в главном класса статический метод просмотра списка.//---------------------------------------------clacc Place{.........static void Print_list();char *name[30];...........}void Persona::Print_list() { cout<<"-------------------------------------------\n"; Place *temp; temp=begin; printf("There are places in list :\n"); while(temp) { cout<<typeid(*temp).name()<<" "<<temp->name<<"\n"; temp=temp->next; }}//---------------------------------------------Всё замечательно работает. Показываются названия городов и тип объектов списка.И зачем тут виртуальность, если можно обойтить статическим методом базового класса?
Неизвестный
20.09.2006, 14:20
общий
Конечно, замечательно. Потому, что и у Area и у Town есть имя. И процедура вывода имени не отличается для разных наследников.virtual void Place::showName();void Area::showName(){cout << "Area: " << name; }void Town::showName(){cout << "Town: " << name; } А вот Print_list() можно делать для базового объекта. И вызывать в нем уже temp->ShowName();
Неизвестный
20.09.2006, 15:15
общий
Думаю, тему можно закрывать.
Форма ответа