nikedeforest
|
|
« : 19-02-2006 00:09 » |
|
Начну наверное с кода class_line.h #include "stdafx.h"
namespace CLine_sp { struct PointInfo { int NumPoints; int **APoints; }; class CLine { protected: int **APoints; int NumPoints;
public: CLine(); virtual bool Approximate()=0; void NewPoint(int x, int y); void GetPointInfo(PointInfo* PInfo); ~CLine();
}; ///////////////////////// class CBezye: public CLine { public: bool Approximate();
};
class CLagranj:public CLine { public: bool Approximate(); };
}
class_line.cpp #include "stdafx.h" #include "class_line.h"
namespace CLine_sp { CLine::CLine() { NumPoints=0; APoints=NULL; }
CLine::~CLine() { if(NumPoints>0) { delete (*APoints); delete (APoints); } }
void CLine::NewPoint(int x, int y) { int **temp=NULL; int i=0; if(NumPoints>0) {
//int ::**temp=NULL; temp=new int* [2]; temp[0]=new int [NumPoints]; temp[1]=new int [NumPoints];
for( i=0;i<NumPoints;i++) { temp[0][i]=APoints[0][i]; temp[1][i]=APoints[1][i]; } delete(*APoints); delete (APoints); APoints=NULL; }
APoints=new int* [2]; APoints[0]=new int[++NumPoints]; APoints[1]=new int[NumPoints];
for(i=0; i<NumPoints-1;i++) { APoints[0][i]=temp[0][i]; APoints[1][i]=temp[1][i]; } APoints[0][NumPoints-1]=x; APoints[1][NumPoints-1]=y;
if(NumPoints>1) {delete (*temp); delete (temp);} } void CLine::GetPointInfo(PointInfo* PInfo) { PInfo->NumPoints=NumPoints; PInfo->APoints=APoints; }
bool CBezye::Approximate() { MessageBox(NULL,"bezye","",0); char s[5]; itoa(NumPoints,s,10); MessageBox(NULL,s,"NumPoints",0); return true; }
bool CLagranj::Approximate() { MessageBox(NULL,"lagranj","",0); return true; } }
1.cpp LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { using namespace CLine_sp; static CLine *obj_line, line; static CBezye bezye; // static int i=0; PointInfo PInfo; int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
switch (message) { case WM_CREATE: obj_line=&line; break;
case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; case IDM_EXIT: DestroyWindow(hWnd); break;
case ID_APPROXIMATE_BEZYE: { obj_line=&bezye; ((CBezye *)obj_line)->Approximate(); break; } default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_LBUTTONDOWN: obj_line->NewPoint(LOWORD(lParam),HIWORD(lParam)); InvalidateRect(hWnd,NULL,true);
break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: Add any drawing code here... obj_line->GetPointInfo(&PInfo); for( i=0;i<PInfo.NumPoints;i++) { SetPixel(hdc,PInfo.APoints[0][i],PInfo.APoints[1][i],0xAA); SetPixel(hdc,PInfo.APoints[0][i]+1,PInfo.APoints[1][i],0xAA); SetPixel(hdc,PInfo.APoints[0][i]-1,PInfo.APoints[1][i],0xAA); SetPixel(hdc,PInfo.APoints[0][i],PInfo.APoints[1][i]+1,0xAA); SetPixel(hdc,PInfo.APoints[0][i],PInfo.APoints[1][i]-1,0xAA); SetPixel(hdc,PInfo.APoints[0][i]+1,PInfo.APoints[1][i]+1,0xAA); SetPixel(hdc,PInfo.APoints[0][i]-1,PInfo.APoints[1][i]+1,0xAA); SetPixel(hdc,PInfo.APoints[0][i]-1,PInfo.APoints[1][i]-1,0xAA); SetPixel(hdc,PInfo.APoints[0][i]+1,PInfo.APoints[1][i]-1,0xAA); } EndPaint(hWnd, &ps); break; case WM_DESTROY: obj_line=NULL; delete (obj_line); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
Теперь постараюсь пояснить. Хочу сразу предупредить, что прблемы созданные здесь, созданы с целью повышения знаний по ООП. И так. Программа должна выполнять построение кривых двумя методами - Безье и Лагранжа. Для построения кривой по методу Безье должен (по моей задумке) использоваться метод bool Approximation() класса CBezye, по методу Лагранжа - метод bool Approximation() класса CLagranj. Пользователь ставит точки по которым и будет производится построение, значение координат заносится в массив APoints. В переменной NumPoints содержится кол-во точек. Заранее неизвестно каким именно методом пользователь захочет строить эти кривые. Можно было бы сделать так, чтобы в каждом классе (CBezye и СLagranj) содержался свой массив APoints. Но в них ведь будут содержаться одинаковые значения, к тому же хотелось бы использовать преимущества ООП, поэтому я хотел бы сделать что-то вроде следующего. В родительском классе CLine содержится массив APoints. А метод Approximation() классов CBezye и CLagranj могли бы использовать значения массива APoints. Только я не знаю как бы это все првильно сделать. Т.е. я хотел бы чтобы было прмерно так: занесли в APoints значения СLine *obj->NewPoints() А затем вызываю метод Approximation либо класса CBezye либо СLagranj, который мог бы использовать значения APoints. В том, что я сейчас навоял есть следующее: В WM_CREATE создается объект класса-родителя СLine В WM_LBUTTONDOWN происходит заполнение массива В WM_PAINT происходит вывод точек. А вот затем, я хотел бы, чтобы при обработки нажатия меню произошло примерно следующее CBezye obj; obj.Approximation() Но только, как я понимаю (да вроде так и происходит) объект obj не располагает значениями, которые заносились до этогот через объект класса CLine. Как же мне провернуть эту затею?
|
|
« Последнее редактирование: 19-02-2006 00:18 от nikedeforest »
|
Записан
|
ещё один вопрос ...
|
|
|
Alf
Гость
|
|
« Ответ #1 : 19-02-2006 00:26 » |
|
Все разом, само собой, ниасилил, нужно тщательно вчитываться. Пока при поверхностном чтении возникло два замечания:
1. В самом начале программы объявлена структура PointInfo, а затем в классе CLine объявлены такие же члены-переменные. Почему бы не агрегировать структуру в класс, раз уж все равно объявлена?
2. Класс CLine является абстрактным, так как в его составе имеется чисто виртуальна функция. Создать экземпляр этого класса компилятор не позволит, можно создать лишь указатель на базовый класс, которому затем присвоить ссылку на экземпляр одного из дочерних классов.
А вообще в таких случаях гораздо лучше нарисовать диаграмму классов. На ней все видно нагляднее, не нужно вчитываться в исходный текст, да и не зависит от языка реализации.
|
|
|
Записан
|
|
|
|
nikedeforest
|
|
« Ответ #2 : 19-02-2006 00:31 » |
|
Про пункт 2 я в курсе, просто сейчас экспериментирую . Про пункт 1, я так никогда не делал, сейчас попробую. А вообще в таких случаях гораздо лучше нарисовать диаграмму классов. На ней все видно нагляднее, не нужно вчитываться в исходный текст, да и не зависит от языка реализации.
Ну а суть того, что я хочу понятна? Я старался словесно все расписать, чтобы в код всматриваться даже не пришлось. Код кинул на всякий случай. Alf, я диаграммы классво никогда не рисовал. Структурные схемы рисовал . Диаграммы рисуются с помощью UML?
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
Alf
Гость
|
|
« Ответ #3 : 19-02-2006 00:46 » |
|
Alf, я диаграммы классво никогда не рисовал. Структурные схемы рисовал . Диаграммы рисуются с помощью UML? Да, в данном случае речь идет о статической диаграмме классов. Очень простая и наглядная картинка, суть схватываешь сразу, не влазя в дебри описаний. А теперь еще замечания. 3. Насколько я понял, назначение структуры PointInfo таково: она содержит счетчик точек, образующих ломаную, и указатель на массив координат этих точек. Если это так и есть, это никуда не годится. Дело в том, что в структуре все члены открыты, поэтому нет никакой гарантии, что количество элементов массива в точности совпадает со значением счетчика. А если в какой-то момент и совпадает, то нет гарантии, что это хрупкое равновесие не будет нарушено глупой ошибкой в программе. Например, если ты по ошибке напишешь NumPoints = 0; , то твой деструктор уже никогда не уничтожит массив, даже если там миллион точек. Вывод: для хранения списка точек нужно создать отдельный класс, а не структуру. Кроме того, список точек лучше хранить не в массиве, а в каком-либо контейнере, который может динамически менять размер. Это позволит на ходу легко добавлять и убирать точки. 4. Саму точку тоже лучше сделать классом, а не массивом из двух чисел. Тем более вдруг завтра тебе заблагорассудится строить кривые не на плоскости, а в пространстве?.. 5. Ты заранее не можешь знать, что выберет пользователь - Кривую Безье или Лагранжа. Раз так, создавай их динамически через вызов new, а не заготавливай впрок, авось пригодится. Тем более что арсенал кривых может впоследствии расшириться. Вот пока информация к размышлению...
|
|
« Последнее редактирование: 19-02-2006 00:50 от Alf »
|
Записан
|
|
|
|
nikedeforest
|
|
« Ответ #4 : 19-02-2006 01:03 » |
|
Вывод: для хранения списка точек нужно создать отдельный класс, а не структуру. Кроме того, список точек лучше хранить не в массиве, а в каком-либо контейнере, который может динамически менять размер. Это позволит на ходу легко добавлять и убирать точки.
По контейнер понятно все, все никак не приучу себя к STL (вроде бы и удобно, а все равно что-то во мне противится этому , ну да ладно это я осилю ). Класс для точки вместо структуры - это по сути будет тажк структура только более защищенная. Я так понял? Т.е. там будет контейнер, счетчик, методы Get и Set. Правильно? По пункту 4 не совсем понял. По идеи то, что ты советуешь в пункте 3 будет ведь содержать массив (контейнер) точек, зачем мне класс для отдельной точки? Или я не так понял? По пункту 5. Ты имеешь ввиду CBezye *obj; obj=new CBezye; ? Если да, то я согласен на все 100%. Меня все же интересует из любопытсва. Если не вносить изменения в программу о которых ты говорил, как же все-таки сделать то что я хотел? По сути ведь я хочу сделать что-то типа этого: СLine *obj=new CLine; obj->NewPoints(x,y); // заносим значения в массив ((СBezye*)obj)->Approximation();
Кстати я так еще не пробовал, но по идеи не должно сработать, этим кодом я просто пытался пояснить что я хочу сделать (научиться делать). P.S. я конечно сделаю изменения о которых ты говоришь, но мне все таки очень хочется найти ответ на мой вопрос.
|
|
« Последнее редактирование: 19-02-2006 01:06 от nikedeforest »
|
Записан
|
ещё один вопрос ...
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #5 : 19-02-2006 01:13 » |
|
А вы ещё не спите? nikedeforest, у тебя книжка есть по ООП? "Надо приобресть" (с) Alf, в 5ом пункте говорит о полиморфизме, и он уже сказал, что CLine абстрактный класс. Всё что требуется: CLine *obj = new CBezye; или CLine *obj = new CLagranj; сорри конечно, что встрял, просто перед выключением решил заглянуть.
|
|
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
nikedeforest
|
|
« Ответ #6 : 19-02-2006 01:16 » |
|
Народ, тот код, что я написал оказывается работает . Обалдеть , как сразу не доехал.
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
Alf
Гость
|
|
« Ответ #7 : 19-02-2006 01:16 » |
|
По контейнер понятно все, все никак не приучу себя к STL (вроде бы и удобно, а все равно что-то во мне противится этому , ну да ладно это я осилю ). Без этого добавление/удаление точек превратится в кошмар. Класс для точки вместо структуры - это по сути будет тажк структура только более защищенная. Я так понял? Т.е. там будет контейнер, счетчик, методы Get и Set. Правильно? Не совсем. Речь пока идет об одной точке, контейнер здесь не потребуется. Контейнер нужен там, где у нас список точек, то есть - прямая. По пункту 4 не совсем понял. По идеи то, что ты советуешь в пункте 3 будет ведь содержать массив (контейнер) точек, зачем мне класс для отдельной точки? Или я не так понял? См предыдущий пункт. По пункту 5. Ты имеешь ввиду CBezye *obj; obj=new CBezye; ? Если да, то я согласен на все 100%. Немного не так. Более гибкий вариант: CLine *obj; ... if (выбрана кривая Безье) obj = new CBesier(...); else if (выбрана кривая Лагранжа) obj = new CLagrange(...); ...
То есть задействовать полиморфизм. Наш указатель имеет тип указателя на родительский класс, а фактически указывает на один из производных, выбранных динамически в процессе выполнения программы. Механизм динамической диспетчеризации сам выберет впоследствии, какие виртуальные функции будут вызваны при разыменовании указателя.
|
|
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #8 : 19-02-2006 01:29 » |
|
Вот набросал эскиз диаграммы классов для программы: Как видишь, каритнка компактная, но из нее видно абсолютно все.
|
Line.gif (6.99 Кб - загружено 2159 раз.)
|
|
Записан
|
|
|
|
Olegator
|
|
« Ответ #9 : 19-02-2006 01:36 » |
|
Alf, на чём ты её сделал? Чем отличается ромбик от стрелочки? Зачем нужна единица и точка?
|
|
|
Записан
|
|
|
|
nikedeforest
|
|
« Ответ #10 : 19-02-2006 01:38 » |
|
Джон, я же выше написал, чтобы на virtual bool Approximation()=0; не обращали внимание - это я так экспериментирую тут у себя и тем самым я делал намек, что рассматриваю как вариант абстрактного родительского класса, так и не абстрактного. В конечном итоге у меня сейчас родительский класс не абстрактный, т.е. строчку virtual bool Approximation()=0; я откинул. А затею я реализовал так, как написал в конце четвертого поста этой темы. А книга по ООП у меня есть .
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
nikedeforest
|
|
« Ответ #11 : 19-02-2006 01:40 » |
|
Alf, я так понимаю, что контейнер точек будет храниться в классе CLine?
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
Alf
Гость
|
|
« Ответ #12 : 19-02-2006 01:48 » |
|
На всякий случай небольшой экскурс по диаграмме классов для тех, кто раньше не имел дело с UML.
1. Каждый класс представляется в виде прямоугольника, разделенного на 3 части.
2. В верхней части записывается имя класса.
3. В средней части перечислены переменные класса.
4. В нижней части перечислены методы класса.
5. Если имя класса выделено курсивом, это означает, что класс абстрактный, и его экземпляр создать невозможно.
6. Если имя метода выделено курсивом, это означает, что метод - чисто виртуальная функция, и он должен быть определен в одном из классов-наследников.
7. Для переменных указывается тип, для методов - типы формальных параметров и возвращаемого значения (если есть).
8. Перед каждым членом класса указывается один из атрибутов видимости: - - private; + - public; # - protected.
9. Линия с ромбиком на одном конце означает отношение агрегации, т.е. класс на той стороне, где ромб, включает в себя какое-то количество экземпляров класса на другой конце линии. Количество экземпляров указывается на концах связи (* - произвольное количество). В данном случае агрегат указывает, что Линия может включать в себя произвольное количество Точек.
10. Линия с незакрашенной стрелкой на конце указывает на отношение наследования между классами, при этом стрелка указывает в направлении от производного класса к базовому.
Вот так вкратце читается диаграмма классов UML. Конечно, есть масса нюансов, но в качестве первого приближения пойдет.
|
|
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #13 : 19-02-2006 01:55 » |
|
Alf, на чём ты её сделал? MS Visio Professional 2003. Не лучший вариант, но когда нужно лишь проиллюстрировать идею, не разрабатывая вглубь, вполне годится. Для серьезных применений использую XDE от Rational Software. Чем отличается ромбик от стрелочки? См. выше. Зачем нужна единица и точка? Цифры и символы на концах отношения агрегирования указывают "мощность" данного отношения. В данном случае это значит следующее. Единица со стороны класса Line указывает, что каждая точка может принадлежать только одной линии. Звездочка со стороны класса Point указывает, что линия может состоять из произвольного количества точеек (включая нуль).
|
|
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #14 : 19-02-2006 01:57 » |
|
Alf, я так понимаю, что контейнер точек будет храниться в классе CLine? Правильно. Вообще хорошо спроектированная ОО программа должна представлять собой модель предметной области, для которой она написана. Каждая сущность предметной области должна быть отражена соответствующим классом. В данном случае мы говорим, что линия состоит из набора точек, что вполне соответствует действительности.
|
|
|
Записан
|
|
|
|
Olegator
|
|
« Ответ #15 : 19-02-2006 01:59 » |
|
Единица со стороны класса Line указывает, что каждая точка может принадлежать только одной линии.
Почему так сделали? Почему не сделать так, чтобы одна точка могла принадлежать многим линиям?
|
|
« Последнее редактирование: 19-02-2006 02:03 от Olegator »
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #16 : 19-02-2006 02:04 » |
|
Не вижу смысла усложнять задачу без необходимости. Если точка будет принадлежать неограниченному количеству линий, это сделает крайне сложной модификацию координат. Да и удалить точку с линии будет не так просто, вдруг она входит в другую линию. Программа станет более запутанной, менее надежной, и при этом никаких плюсов не получит взамен.
Вообще при программировании нужно придерживаться необходимого минимализма - если задача решается просто, ни к чему добавлять искусственные сложности, которые не приносят реальной пользы.
|
|
|
Записан
|
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #17 : 19-02-2006 12:38 » |
|
В конечном итоге у меня сейчас родительский класс не абстрактный, т.е. строчку virtual bool Approximation()=0; я откинул.
Зря, в первом случае ты сделал всё абсолютно правильно, именно так и должно быть. Общие данные в родительском классе и различные операции обработки или использования этих данных в дочерних классах. Потом, почему "рисование" у тебя присходит в WndProc? Не лучше ли объекту поручить эту задачу? По ООПшному у тебя реакция на WM_PAINT должно выглядеть примерно так: case WM_PAINT: HDC hdc = BeginPaint(hWnd, &ps); obj->Draw(hdc); EndPaint(hWnd, &ps);
Ну и так далее. Просто замечание, а не проще ли начать исследование ООП с чего-нибудь попроще? Кривые Безье сами по себе вещь не тривиальная. Сделай сначала простые геометрические фигуры - линия, прямоугольник, круг, треугольник с родительским классом, например, CFigure и набором функций. Для "количества" достаточно будет простого массива указателей на CFigure. "Усвой" полиморфизм. Не надо отвлекаться на STL и сложные формулы, которые к ООП никакого отношения не имеют (хотя по большому счёту надо бы с этого начать, как с основных структур, зачем тебе сразу STL? Сделай для начала, например, свой собственный связаный список, потом можно сделать из него шаблон - после этого ты поймёшь проблематику и будешь знать на что такие структуры способны, что они могут делать. После этого и STL будет просто хорошей библиотекой, а не чёрным ящиком). Вообще при программировании нужно придерживаться необходимого минимализма - если задача решается просто, ни к чему добавлять искусственные сложности, которые не приносят реальной пользы.
Alf, как там было в Матане: "необходимо и достаточно"? nikedeforest, если ты ставишь задачу усвоить получше ООП, то не надо вводить дополнительные сложности и непонятности, загромождать всё дополнительным кодом, необязательным для выполнения основной задачи. Вообще то самыми хорошими классами для начала изучения ООП ИМХО являются Дробь и Строка. Перегрузка операторов, функций, инкапсуляция данных. И только потом переходить к наследованию, полиморфизму.
|
|
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #18 : 19-02-2006 12:49 » |
|
Просто разбил на два поста - теперь ближе к телу А затею я реализовал так, как написал в конце четвертого поста этой темы.
А как именно? По идее ты всё делал правильно и CLine абстрактный, и "желание" у тебя было правильным. СLine *obj=new CLine; obj->NewPoints(x,y); // заносим значения в массив ((СBezye*)obj)->Approximation();
только это должно так выглядеть: СLine *obj=NULL; if(пользователь выбирает Безье) obj = new CBezye; else obj = new CLagranj;
obj->NewPoints(x,y); obj->Approximation(); obj->Draw(); ...
|
|
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
nikedeforest
|
|
« Ответ #19 : 19-02-2006 14:06 » |
|
Джон, вот почти рядом. Сейчас по идеи уже будет проще нам общаться. Я понимаю, что правильно - делать абстрактный класс, но как вы все справедливо заметили, нельзя создать объект абстрактного класса. Я понимаю, что вы (Джон, Альф) мне говорите, но мне кажется вы не все совсем поняли, что у меня к чему. Я вот как думал сделать: Запускается программа и создается объект родительского класса, т.е. CLine *obj=new CLine; Потом пользователь на рабочей области окна щелкает мышкей тем самым назначая контрольные точки. Функция NewPoints() всего навсего заполняет массив тех самых контрольных точек. Т.е. пользователь щелкнул по области окна и координаты щелчка занеслись в массив. Пользователь поставит таких точек всего 3-4 (на желание). После того, как пользователь поставил эти контрольные точки он решает с помощью какого алгоритма будет строится кривая, либо методом Безье, либо методом Лагранжа. Заострите здесь внимание пожалуйста. Сначала ставим точки и только потом решаем каким алгоритмом строить. Если делать как предлагаете вы (это конечно верно с точки зрения ООП), то пользователь сначала должен выбрать алгоритм построения кривой и только потом он будет ставить контрольные точки. Так ведь получается? Если же делать как вы, но реализовывать то, что я запланировал, то здесь 2 варианта: 1) Создать глобальный массив этих контрольных точек. Когда пользователь будет ставить контрольные точки, то будет заполняться глобальный массив. Затем, когда пользователь выберет алгоритм, то как раз тогда создастся объект нужного дочернего класса и в него уже надо будет передать этот массив контрольных точек. 2)Сразу создать 2 объекта (по одному для каждого дочернего класса). Когда пользователь будет ставить контрольные точки, то для каждого объекта производить заполнение массива контрольных точек. Теперь должно многое проясниться. Мне ведь как раз интересно унать как сделать так, чтобы все объекты дочерних классов располагали одним на всех массивом APoints. Понимаете? Это, думаю, можно было бы достичь, сделав массив APoints статическим (static). Я прав? Если да, то хорошо, но можно ли сделать по-другому? Я пока вот как делаю: class CLine { protected: int **APoints; int NumPoints;
public: CLine();
void NewPoint(int x, int y); void GetPointInfo(PointInfo* PInfo); ~CLine();
}; ///////////////////////// class CBezye: public CLine { public: bool Approximate();
};
class CLagranj:public CLine { public: bool Approximate(); };
Описание методов приводить не буду, думаю не в этом соль. Приведу как дальше работаю с классами. CLine *obj_line, line; obj_line=&line; obj_line->NewPoint(LOWORD(lParam),HIWORD(lParam));
if(пользователь выбирает Безье) ((CBezye *)obj_line)->Approximate(); else ((CLagranje*)obj_line)->Approximate();
Ну теперь, думаю вам должно стать совсем понятно (иначе я уж и не знаю как объяснить) Вы поймите, я хочу достичь следующего: -пользователь сначала ставит точки и только потом выбирает алгоритм построения -не хотелось бы для объекта каждого дочернего класса производить заполнение массива APoints -не хотелось бы использовать глобальный (или локальный статический) массив, не принадлежащий классу. -По сути хочу сделать так, чтобы массив APoints был одинаков для всех дочених классво (млин, наверное static напрашивается) Как правильно-то сделать, вариантов ведь много, но наилучший должен быть один (на то он и наилучший )
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
nikedeforest
|
|
« Ответ #20 : 19-02-2006 14:24 » |
|
Тоже решил разбить на 2 поста . Этот пост меньше к делу, в нем я хочу пояснить свои мотивации . Просто замечание, а не проще ли начать исследование ООП с чего-нибудь попроще? Кривые Безье сами по себе вещь не тривиальная. Сделай сначала простые геометрические фигуры - линия, прямоугольник, круг, треугольник с родительским классом, например, CFigure и набором функций.
Джон, я ознакомился с теорией по ООП и многое усвоил (я в курсе был про абстрактные классы ), но теория без практики ничего не дает. И вот я начинаю (точнее продолжаю)практиковаться. НО. У меня не так много времени, чтобы выдумать себе задания и я стараюсь оттачивать свои знания на том, что мне подкидывают. Т.е. я стараюсь совмещать. Это программа по сути легко решается без всякого ООП и я уже давно бы про нее забыл. Но я решил использовать ООП, чтобы было впрок. Надеюсь я найду понимание и не получу упреков типа того, что зачем нагромаждать. Лучше нагромаждать сейчас, пока учусь, чем потом, когда придется работать по специальности (если конечно повезет). Не надо отвлекаться на STL и сложные формулы, которые к ООП никакого отношения не имеют (хотя по большому счёту надо бы с этого начать, как с основных структур, зачем тебе сразу STL?
Вот именно в этой программе я не захотел отвлекаться на STL. Я немного уже поработал с STL и оценил ее (т.е. удобно ), но пока для меня работа с STL означает брать книгу и смотреть как там что делать (потому как в памяти еще не отложилось), в данном случае не хотелось отвлекаться. Вроде бы все. Сильно не пинайте .
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
Михалыч
|
|
« Ответ #21 : 19-02-2006 16:53 » |
|
Пардон, господа, что влезаю У вас и так хорошо получается Но мои 5 копеек так и жгут руку. Так что я их таки закину Мне лично импонирует идея глобального массива точек, м.б. и статического, хотя и не обязательно. И указатель на массив в любом дочернем классе. Во всяком случае я не стал бы заводить сразу 2 объекта дочерних классов и заполнять каждому из них свой массив. Ладно бы еще когда их только 2...3...10. А если их фиг знает сколько - читай очень много? А еще лучше вообще неизвестно сколько. Создавать динамически по мере надобности - мне так кажется не хотелось бы использовать глобальный (или локальный статический) массив, не принадлежащий классу. -По сути хочу сделать так, чтобы массив APoints был одинаков для всех дочених классво (млин, наверное static напрашивается) Как правильно-то сделать, вариантов ведь много, но наилучший должен быть один (на то он и наилучший ) За что такая нелюбовь к глобальным массивам? Ну и в варианте 1 - массив и будет одинаков для всех объектов. А про лучший вариант - не-а, он не будет один. Скорее всего для разных групп индивидов он будет свой (ну, у всех ведь свои предпочтения) и чем-то он будет лучшим. Да еще и для разных вариантов применения тоже возможно, что лучшими будут разные варианты реализации. Все это ИМХО, само собой Вроде бы все. Пинать нЕ за что
|
|
|
Записан
|
Поживем - увидим... Доживем - узнаем... Выживу - учту
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #22 : 19-02-2006 17:40 » |
|
Сразу, вот за это пинать не будем: CLine *obj_line, line; obj_line=&line; obj_line->NewPoint(LOWORD(lParam),HIWORD(lParam));
if(пользователь выбирает Безье) ((CBezye *)obj_line)->Approximate(); else ((CLagranje*)obj_line)->Approximate();
- лучше сразу расстреляем из крупнокалиберного пулемёта, чтоб не мучился Я даже не знаю как это назвать? Анти-ООП пожалуй слишком мягко. Про остальное - теперь всё понятно. Это программа по сути легко решается без всякого ООП ЛЮБАЯ ЗАДАЧА РЕШАЕТСЯ БЕЗ ВСЯКОГО ООП! разница только в "легко или нет" Если например нужна функция возведения целого числа в квадрат, то зачем городить объект? Если ты в данном случае хочешь: 1 получить данные 2. посторить по этим данным два графика разного типа, причём не одновременно, то я вижу задачу для одного массива данных (можно даже в виде объекта) и двух функций. Эти функции могут быть членами этого объекта. А так даже не знаю, какие преимущества ты получишь используя ООП? ООП с наследованием, а именно это, если я правильно понял, ты и хочешь попробовать на примере, себя оправдает в случае, если нужно рисовать много разнотипных объектов "одновременно", по той простой причине, что можно создавать списки (массивы) однотипной информации. Примитивный случай - у тебя есть объект типа Точка с Х и У координатами. Он знает как себя рисовать. Тогда ты создаёшь список list<CТочка> и счастлив. А как быть, если в одном случае надо рисовать треугольник, в другом круг и тд и тп? Тогда и приходит на помощь полиморфизм. С возможностью создать список одного типа - CFigure. list<CFigure> lst; // инициаллизация объектов public CFigure и вставка их в список list<CFigure*>::iterator i = lst.begin(); while(i!=lst.end()) { (*i)->Draw(pDC); i++; }
|
|
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
Alf
Гость
|
|
« Ответ #23 : 19-02-2006 18:19 » |
|
Я понимаю, что вы (Джон, Альф) мне говорите, но мне кажется вы не все совсем поняли, что у меня к чему. Поначалу - да, задача не слишком четко сформулирована. Ты только сейчас уточнил, что нужно делать на самом деле, и решение видится мне уже другим. Я вот как думал сделать: Запускается программа и создается объект родительского класса, т.е. CLine *obj=new CLine; Потом пользователь на рабочей области окна щелкает мышкей тем самым назначая контрольные точки. Функция NewPoints() всего навсего заполняет массив тех самых контрольных точек. Т.е. пользователь щелкнул по области окна и координаты щелчка занеслись в массив. Пользователь поставит таких точек всего 3-4 (на желание). После того, как пользователь поставил эти контрольные точки он решает с помощью какого алгоритма будет строится кривая, либо методом Безье, либо методом Лагранжа. Заострите здесь внимание пожалуйста. Сначала ставим точки и только потом решаем каким алгоритмом строить. Вот оно, самое главное, с чего нужно было начинать. Одна и та же линия может иметь несколько представлений: Безье, Лагранжа, возможно, также ты захочешь вывести ее в виде ломаной, потом добавить еще какие-то сплайны... При этом набор исходных точек не меняется. Если делать как предлагаете вы (это конечно верно с точки зрения ООП), то пользователь сначала должен выбрать алгоритм построения кривой и только потом он будет ставить контрольные точки. Так ведь получается? Именно так. Если в момент создания кривой заранее известно, какого типа она должа быть, предложенный нами вариант оптимален. Если метод сглаживания выбирается позже, он не годится. Еще одна иллюстрация того, насколько важно иметь качественную спецификацию программы до того, как приступать к проектированию. Если же делать как вы, но реализовывать то, что я запланировал, то здесь 2 варианта: 1) Создать глобальный массив этих контрольных точек. Когда пользователь будет ставить контрольные точки, то будет заполняться глобальный массив. Затем, когда пользователь выберет алгоритм, то как раз тогда создастся объект нужного дочернего класса и в него уже надо будет передать этот массив контрольных точек. Если хочешь овладеть хорошим стилем ООП, забудь о глобальных объектах. Тем более что болеепригодные для ООП языки - C# и Java - вообще не позволят тебе создать ничего глобального, там в самом языке нет нужных для этого средств. Глобальные объекты столь же чужды для ООП, сколь оператор GOTO для структурного программирования. 2)Сразу создать 2 объекта (по одному для каждого дочернего класса). Когда пользователь будет ставить контрольные точки, то для каждого объекта производить заполнение массива контрольных точек. Я вижу более изящное решение. Изложу его чуть позже. Теперь должно многое проясниться. Мне ведь как раз интересно унать как сделать так, чтобы все объекты дочерних классов располагали одним на всех массивом APoints. Понимаете? Это, думаю, можно было бы достичь, сделав массив APoints статическим (static). Я прав? Если да, то хорошо, но можно ли сделать по-другому? Можно. См. выше. Я пока вот как делаю: ... if(пользователь выбирает Безье) ((CBezye *)obj_line)->Approximate(); else ((CLagranje*)obj_line)->Approximate(); ...
Солидарен с Джоном - форменное безобразие. Бессмысленная и опасная конструкция. Как правильно-то сделать, вариантов ведь много, но наилучший должен быть один (на то он и наилучший ) Не должен. Каждый вариант обладает своими особенностями, и выбор зависит от того, как ты расставишь приоритеты. В одних случаях критично быстродействие (если тебе нужно построить миллион кривых, даже лишняя миллисекунда имеет значение). В других необходимо минимизировать память любой ценой (например, программа должна работать на карманном компьютере или сотовом телефоне). Лично я во главу угла ставлю ясность программы, удобство отладки и сопровождения и легкую расширяемость, если явно заказчиком не предъявляются другие требования. Свое в идение решения изложу чуть позже.
|
|
|
Записан
|
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #24 : 19-02-2006 18:34 » |
|
зы Забыл, Михалыч, ты это... заходи, плюрализм мнений в вопросах обучения и объяснения особенно важен. ИМХО.
|
|
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
Alf
Гость
|
|
« Ответ #25 : 19-02-2006 19:31 » |
|
Вот мое предложение: Вкратце суть: 1. Класс CLine - обычный, неабстрактный. Класс включает в себя набор точек, образующих ломаную. 2. Класс CLine включает экземпляр абстрактного класса CApproxymation. 3. Класс CApproxymation включает единственный абстрактный метод Approxymate. 4. Класс CApproxymation имеет двух (в данный момент) потомков - неабстрактные классы CApproxBezier и CApproxLagrange. Их назначение - задать соответствующую реализацию метода Approxymate. 5. Если понадобится добавить какой-то еще алгоритм аппроксимации (назовем его условно XSpline), в программе не придется менять абсолютно ничего. Нужно будет лишь дописать новый класс CApproxXSpline, сделать его производным от CApproxymation и реализовать метод Approxymate. В этом и состоит золотое правило проектирования - строить приложение таким образом, чтобы при добавлении новых функций не менялся существующий код, а лишь добавлялся новый, реализующий новые функции. Для подобных крошечных проектов это может оказаться не столь важным, а для крупных и ответственных, когда после каждого внесенного изменения приходится тратить много усилий на тестирование, цена каждого изменения оказывается высокой. В данном случае можно ограничиться тестированием лишь нового кода, что существенно проще. Работа с классом CLine: 1. Заполняем список точек. 2. Создаем нужный нам вариант класса-аппроксиматора внутри CLine. 3. Вызываем его метод Approxymate. На мой взгляд, именно такой подход ближе всего к идеалу, т.к. он прост, легко реализуется и при необходимости очень легко расширяется дополнительными вариантами аппроксимации без переписывания прежнего кода.
|
|
« Последнее редактирование: 19-02-2006 19:35 от Alf »
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #26 : 19-02-2006 19:56 » |
|
Мне лично импонирует идея глобального массива точек, м.б. и статического, хотя и не обязательно. ... За что такая нелюбовь к глобальным массивам? А зачем тогда использовать объекты вообще? Вытащить все переменные наружу, свалить в одну большую глобальную кучу - и делай, что хочешь. Красота, никаких ограничений. Может, конечно, это и излишне, но все же напомню, что инкапсуляция данных является одной из основных идей ООП. Доступ к данным ограничивается до необходимого минимума. Обычно менять их значения могут лишь члены класса, которому принадлежат данные. Зачем это нужно, вряд ли имеет смысл объяснять.
|
|
|
Записан
|
|
|
|
Alf
Гость
|
|
« Ответ #27 : 20-02-2006 08:36 » |
|
Вдогонку к вышесказанному. Только что мне вернули книжку "Приемы объектно-ориентированного проектирования" Банды Четырех. Проверил, не подвела ли меня память. Предложенный мной вариант именуется у них паттерном "Strategy" и описан на странице 300. Если кто еще не обзавелся этой книгой (и очень много потерял), напоминаю, что она есть в нашей библиотеке: https://club.shelek.ru/download.php?id=327Не советую пренебрегать ей, т.к. знание паттернов и умение их применять - одно из отличий профессионала от любителя. Еще о паттерне "стратегия" можно узнать здесь (фактически это глава из той самой книги без примеров кода): http://ooad.asf.ru/patterns/patterninfo.asp?ID=22
|
|
|
Записан
|
|
|
|
nikedeforest
|
|
« Ответ #28 : 20-02-2006 09:25 » |
|
Alf, в целом я понял замысел, но пока еще не реализовал. При реализации наверное новые вопросы появятся. Попробую сегодня реализовать.
|
|
|
Записан
|
ещё один вопрос ...
|
|
|
Михалыч
|
|
« Ответ #29 : 20-02-2006 13:42 » |
|
Мне лично импонирует идея глобального массива точек, м.б. и статического, хотя и не обязательно. ... За что такая нелюбовь к глобальным массивам? А зачем тогда использовать объекты вообще? Вытащить все переменные наружу, свалить в одну большую глобальную кучу - и делай, что хочешь. Красота, никаких ограничений. Может, конечно, это и излишне, но все же напомню, что инкапсуляция данных является одной из основных идей ООП. Доступ к данным ограничивается до необходимого минимума. Обычно менять их значения могут лишь члены класса, которому принадлежат данные. Зачем это нужно, вряд ли имеет смысл объяснять. Вай-вай! Сдается мне, что не все так запущено Я как раз привык отделять данные от их обработки. Привычка - специфика работы с системами сбора и обработки данных. Если очень грубо - данные валятся в базу, а приложение выбирает из нее то, что ему надо и делает с ними то, что надо. Данных может быть много и весьма разнообразных. Пихать их иногда внутрь класса - увольте. Поэтому в данном случае я и рассматривал эту задачу как возможный вариант - данные могут ведь и не только от пользователя приходить. Массив - прототип некой базы данных, где хранится информация о точках. Не вижу криминала. На мой взгляд, ООП применять нужно. Но не фанатично А так, как удобнее. Оно ведь в конце концов призвано жизнь облегчать, а не усложнять. Более того, раз уж пошла "такая пьянка", буду "резать последний огурец" Думаю, не скажу ничего нового - ООП применять нужно не везде. Можно, но иногда не нужно. Но можно. Ведь одно из основных преимуществ, которое дает ООП - повторное использование кода. А если его повторно использовать негде? Тогда мне лично применять ООП просто удобнее (хотя и не обязательно), привычнее, что-ли... Образ мышления уже изменен объектным подходом Однако мне приходится довольно много писать не очень больших программ, где нет "повторимости" кода. Вы скажете - так не бывает... Бывает Конечно, не все "неповторимо и повторно не применимо", но все же... Если проще и быстрее написать приложение с использованием глобального массива - да я и применю его, и не будет у меня голова болеть о том, что я нарушаю "святые каноны" ООП. Опять же оговорюсь - все зависит от задачи Как-то давно, и не помню в какой теме, но абсолютно точно, я уже говорил - что мне, например, паттерны интересны теоритически, но не нужны практически. И STL тоже далеко не всегда нужна. Ну и что? Не профессионально? Зачастую (в системах управления например) - чем проще тем лучше - к вопросу о надежности, отлаживаемости, читабельности и т.п. А вообще - была такая тема - Олегатор ее поднимал - границы применения ООП. Это уже наверное туда
|
|
|
Записан
|
Поживем - увидим... Доживем - узнаем... Выживу - учту
|
|
|
|