Форум кафедры Техники и Электрофизики Высоких Напряжений

Онлайн-сообщество ТВНщиков
Гостям форума:

Добро пожаловать на форум по технике высоких напряжений!
Для получения доступа ко всем разделам необходимо зарегистрироваться


Текущее время: 20 ноя 2018, 16:36

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 27 ]  На страницу 1, 2  След.
Автор Сообщение
 Заголовок сообщения: Домашнее задание №3
СообщениеДобавлено: 19 окт 2017, 21:22 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
Уфф, разболелся я что-то основательно, прошу простить за задержку.

Итак, задание. Ваша задача - реализовать классы на чистом C, после чего еще и реализовать на чистом C виртуальную функцию. Нужно это для понимания полиморфизма изнутри (т.е., если не можете - значит не понимаете).

Начну с классов, которые нужно реализовать. Сначала образец на С++ (немножко адаптированный с занятия):

#include <iostream>
#include <iomanip>
using namespace std;

class Shape
{
public:
     Shape(double x, double y) : x0(x), y0(y) {}
     double CalcArea(); // добавьте здесь virtual и посмотрите как изменится результат. Объясните почему
protected:
     double x0, y0;
};

double Shape::CalcArea()
{
     // По замыслу итоговой программы класс Shape должен быть чисто вирутальным, т.е. функция реализована быть не должна
     return 0;
}

class Circle : public Shape
{
public:
     Circle(double x, double y, double r) : Shape(x, y), r0(r) {};
     double CalcArea();
private:
     double r0;
};

double Circle::CalcArea()
{
     return 3.1415*r0*r0;
}

int main()
{
     Shape** pShapes = new Shape*[2];
     pShapes[0] = new Shape(0, 0);
     pShapes[1] = new Circle(1, 1, 2);
     for (int i = 0; i < 2; i++)
     {
          cout << pShapes[i]->CalcArea() << endl;
          delete pShapes[i];
     }
     delete [] pShapes;
     return 0;
}



Поскольку С знают не все, то, чтобы немного облегчить жизнь, привожу реализацию класса Shape на чистом С.

#include <stdio.h>

struct Shape
{
     double x0;
     double y0;
     double(*pCalcArea)(struct Shape*);
};

void ShapeConstructor(struct Shape* this, double x, double y, double(*CalcArea)(struct Shape*) )
{
     this->x0 = x;
     this->y0 = y;
     this->pCalcArea = CalcArea;
};

double CalcArea(struct Shape* this)
{
     return 0.0;
}

int main()
{
     struct Shape shape_object;
     ShapeConstructor(&shape_object, 0, 0, CalcArea);
     printf("%g\n",shape_object.pCalcArea(&shape_object));
     return 0;
}


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 19 окт 2017, 21:31 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
Теперь о том, как использовать Visual Studio. Создайте внутри Solution два проекта - один на С++, другой на С. Проект на чистом С пусть содержит только один файл с расширением .c
Кроме того, в настойках проекта нужно поменять язык на С:

Изображение


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 19 окт 2017, 21:32 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
Да, и еще. В чистом С нет операторов new и delete, вместо них нужно использовать библиотечные функции malloc и free.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 19 окт 2017, 23:13 
Не в сети
Аватара пользователя

Зарегистрирован: 26 мар 2016, 01:38
Сообщения: 22
Откуда: Москва
Вызов принят!


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 21 окт 2017, 19:52 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
:fire:


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 21 окт 2017, 19:55 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
Меня настораживает отсутствие вопросов :roll:


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 22 окт 2017, 14:17 
Не в сети

Зарегистрирован: 12 окт 2016, 20:08
Сообщения: 14
Здравствуйте, подскажите, пожалуйста, что означат данная строчка:
double(*pCalcArea)(struct Shape*);
?


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 22 окт 2017, 14:45 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
Добрый день!
Эта строчка объявляет переменную pCalcArea, имеющую тип указателя на функцию, которая:
- принимает указатель на структуру Shape
- возвращает double.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 22 окт 2017, 15:42 
Не в сети

Зарегистрирован: 12 окт 2016, 20:08
Сообщения: 14
Спасибо!


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 22 окт 2017, 18:28 
Не в сети

Зарегистрирован: 12 окт 2016, 20:08
Сообщения: 14
А можете подсказать как объяснить компилятору, что Circle - это подкласс Shape?
Чтобы можно было в качестве аргумента использовать struct Circle в строке:
double(*pCalcArea)(struct Shape*)


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 22 окт 2017, 21:02 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
Т.е. как реализовать наследование в С? Нужно вложить структуру Shape в структуру Circle, т.е. сделать Shape членом структуры Circle.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 22 окт 2017, 21:43 
Не в сети

Зарегистрирован: 12 окт 2016, 20:08
Сообщения: 14
Да, тогда структура Circle унаследует переменные структуры Shape.
Но здесь же:
double(*pCalcArea)(struct Shape*)
мы обращаемся к структуре Shape, которая не знает переменных структуры Circle. А для вычисления площади круга надо знать r0.

То есть надо сделать столько указателей на функции, сколько и структур.
Или как-то можно обойтись без этого?

В голову лезет только структура Shape с кейсами.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 22 окт 2017, 23:00 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
Наилучший способ такой. Нужно сделать таблицу (отдельную структуру) указателей на функции для базового класса и таблицу указателей для производного. Указатель на эту таблицу (структуру) хранить в базовом классе. А значение указателю присваивать в засимости от того, объект какого класса в действительности создается.

Теорию полиморфизма можете погуглить по терминам vptr (указатель на таблицу виртуальных функций) и vtbl (сама таблица).

Вообще сейчас я понимаю, что я не рассказывал вам про статические переменные. Можно и без них, но удобно таблицу виртуальных функций создать как static, что обеспечит ей существование в течение всего времени работы программы.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 22 окт 2017, 23:04 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
Да, с применением vptr функцию класса нужно будет вызывать так: shape_object.vptr->CalcArea(pShape). А для указателя на объект класса так: pointer2shape_object->vptr->CalcArea(pShape).


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 22 окт 2017, 23:10 
Не в сети

Зарегистрирован: 12 окт 2016, 20:08
Сообщения: 14
Спасибо! Буду пробовать


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 23 окт 2017, 00:50 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
Могу для подмоги выложить код функции main() на C, которая служит аналогом функции main() на C++ из задания. Нужно?


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 23 окт 2017, 11:30 
Не в сети

Зарегистрирован: 10 сен 2010, 22:09
Сообщения: 278
Откуда: из Д3 я
У меня получилось выполнить задание, но только реализуя "наследование" путем дублирования полей базовой структуры в производных структурах (а не путем вложения "базовой" структуры в производные). После этих общих полей я добавлял специфические для производной структуры поля (радиус для круга, длину стороны для квадрата). Это сохранение порядка следования общих полей (в числе которых и указатель на структуру, хранящую указатели на "виртуальные методы") базовой и производных структур, как я понимаю, и позволило мне по указателю на базовую структуру, указывающему в действительности на экземпляр производной структуры, обращаться к его "таблице виртуальных методов" и вызывать нужную функцию (функцию, написанную для работы с той структурой, на экземпляр которой в действительности указывает указатель). Причем присваивать указателю на базовую структуру указатель на экземпляр производной можно, как производя явное приведение типов, так и не делая этого. В последнем случае компилятор выдаст warning, но программа отработает корректно.

Теперь, собственно, вопрос) Как реализовать то же самое, вложив базовую структуру в производную? Проблема в том, что указатель на структуру-"таблицу виртуальных функций" не будет в таком случае полем производной структуры. Он будет полем базовой структуры, экземпляр которой (или указатель на экземпляр которой) будет в свою очередь полем производной. Поэтому выполнить вызов таким образом
dmatveev писал(а):
pointer2shape_object->vptr->CalcArea(pShape)
в случае, если pointer2shape_object указывает в действительности на экземпляр производной структуры не удастся (у производной структуры нет поля vptr). Кстати, почему в функцию CalcArea передается некий pShape, а не pointer2shape_object?)
Для указателя на производную структуру вызов может выглядеть следующим образом:
pointer2circle_object->shapeParent_p->vptr->CalcArea(pointer2circle_object),
где shapeParent_p — указатель на базовую структуру, являющийся полем производной структуры. Но выполнить вызов таким образом для указателя на базовую структуру, пусть даже указывающего на экземпляр производной структуры (путем явного приведения типов), компилятор мне не дает (shapeParent_p не является полем базовой структуры). Вот такая загогулина) Получается, что механизм виртуальных функций вроде как реализован, но практического толка от него нет (вызывать по указателям на базовую структуру, указывающим на экземпляры производных структур, "виртуальные методы" этих структур у меня не выходит).


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 23 окт 2017, 15:09 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
На самом деле задачу ты решил полностью, и даже больше сделал, разобравшись во всех тонкостях. Просто обрати внимание на то, что вызывать виртуальную функцию по указателю на производный класс в задании и не требуется. Полиморфное поведение - это вызов функций производных классов по указателям на их базовый класс. В задании по указателям на базовый класс вызываются функции как базового так и производного классов.

pShape - мой косяк. Должно быть либо pointer2shape_object->vptr->CalcArea(pointer2shape_object), либо pShape->vptr->CalcArea(pShape) — переименовал в одном месте, а в другом забыл.


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 23 окт 2017, 15:16 
Не в сети
Site Admin

Зарегистрирован: 03 сен 2008, 16:09
Сообщения: 4258
Откуда: Д-3
Для всех остальных приведу код функции main() на С, пусть послужит ориентиром.

int main()
{
     struct Shape** pShapes = malloc(2*sizeof(struct Shape*));
     pShapes[0] = malloc(sizeof(struct Shape));
     pShapes[1] = malloc(sizeof(struct Circle));

     ShapeConstructor(pShapes[0], 0, 0);
     CircleConstructor(pShapes[1], 1, 1, 2);

     for (int i=0; i<2; i++)
     {
          printf("%g\n", pShapes[i]->vptr->pCalcArea(pShapes[i]));
          free(pShapes[i]);
     }
     free(pShapes);
     return 0;
}


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
 Заголовок сообщения: Re: Домашнее задание №3
СообщениеДобавлено: 23 окт 2017, 16:46 
Не в сети

Зарегистрирован: 10 сен 2010, 22:09
Сообщения: 278
Откуда: из Д3 я
dmatveev писал(а):
Просто обрати внимание на то, что вызывать виртуальную функцию по указателю на производный класс в задании и не требуется.
Я это понимаю, речь о вызове по указателю на производную структуру в моем вопросе зашла в попытке изложить суть проблемы)
dmatveev писал(а):
Полиморфное поведение - это вызов функций производных классов по указателям на их базовый класс. В задании по указателям на базовый класс вызываются функции как базового так и производного классов.
Так о том я и спрашиваю) как реализовать такой вызов, если базовая структура вложена в производную? В таком случае структура, выполняющая роль "таблицы виртуальных функций" (или указатель на такую структуру) напрямую в производную структуру не входит, входит как поле вложенной базовой структуры. В таком случае сделать вызов, так как Вы показали в функции main(), у меня не выходит, ведь vptr входит в базовый класс, но не входит напрямую в производный.

pShapes[i]->vptr->pCalcArea(pShapes[i])

Проблемы такой конечно нет, если не вкладывать базовую структуру в производную, а продублировать "руками" её поля, как я и сделал. Но Вы писали, что лучше всего "наследование" реализовывать, вложив базовую структуру в производную. Стало быть, в этом случае тоже возможен "вызов функций производных классов по указателям на их базовый класс". Может я уже зашорился малость и не вижу простого выхода?)


Вернуться к началу
 Профиль Отправить личное сообщение  
Ответить с цитатой  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 27 ]  На страницу 1, 2  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Перейти:  
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB