| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | «  : 18-05-2010 07:47 »  |  | 
 
 То, что ты пытаешься делать, это форменное надругательство над логикой. 
 Очевидно, что я приводил не кусок программы, а адаптированный код. Но тем не менее я не буду оспаривать данное утверждение, очень может быть, что оно верное. Тем более я буду благодарен за любые комментарии, т.к. именно тема дизайна наиболее мне привлекательна. Итак имеем 1. Структуру системного вектора, содержащего указатели на отдельные модули системы struct ZSysVector{
 ZGui*  _gui;
 ZReports*  _reports;
 ....
 AProject*  _prj;
 }
 
Главной идеей системного вектора - локализация описания среды исполнения кода.  Польза: унификация инициализации модулей, сокращение числа передоваемых параметров, сокращение числа интерфейсных прозрачных функций в головных модулях, нет необходимости в многочисленных мелких изменениях при изменении среды. Недостаток: Отсутствие явного указания в коде программы связей между модулями. Есть еще один фактор - дань личным традициям. 2. Необходимо разработать новую подсистему (Simulation), которой требуется добавить к системному вектору пару переменных. Раньше я просто бы добавил эти переменные в системный вектор, но: а) в больших системах системный вектор разрастается до неприятных размеров б) есть очень красивое понятие инкапсуляция, и не хотелсь бы от него отказываться. Поэтому мне показалось естественным использовать наследование struct ZSysVectorSimulation : public ZSysVector{
 SRand*  _rand;
 SMainTree*  _tree;
 }
 
В этом случае мы унифицируем обращение к внешним модулям из внутреннего кода, и внутри модуля нам нет необходимости знать структуру всей программы. В чем здесь ошибка, и где нарушение логики дизайна? 3. Возможны и другие варианты Например  Проще некудаstruct Sb
 {
 Sa aa;
 int ib;
 };
 
 или создать отдельный вектор для каждого модуля или ... Но все известные мне варианты не соответствуют отмеченным выше критериям. 4. Естественно при инициализации новой структуры ZSysVectorSimulation и возникла проблемы, вынесенная в заголовок данной темы. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Алексей++ 
								глобальный и пушистыйГлобальный модератор    Offline 
								Сообщений: 13
								
								
								
								
								
							 | 
								|  | « Ответ #1 : 18-05-2010 07:51 »  |  | 
 
 инициализировать можно через список инициализации конструктора struct ZSysVectorSimulation : public ZSysVector{
 SRand*  _rand;
 SMainTree*  _tree;
 
 ZSysVectorSimulation():ZSysVector(...параметры для передачи в конструктор ZSysVector...)//,_rand(0),_tree(0)
 {
 }
 }
 
 
 |  
						| 
								|  |  
								| « Последнее редактирование: 18-05-2010 07:52 от Алексей1153++ » |  Записан | 
 
 |  |  | 
	| 
			| 
					
						| Вад | 
								|  | « Ответ #2 : 18-05-2010 08:09 »  |  | 
 
 Главной идеей системного вектора - локализация описания среды исполнения кода. 
 Слишком много существительных    Можно поподробнее, для чего это делается? Если модули независимые - то зачем их держать в какой-то одной структуре? Если кто-то внешний управляет их временем жизни - почему бы этого кого-то не инкапсулировать в объект - менеджер модулей? Или, может быть, это все модули знают про такую структуру, и через неё обращаются к другим модулям? 2. Необходимо разработать новую подсистему (Simulation), которой требуется добавить к системному вектору пару переменных.
 В связи с первым вопросом, непонятно также, для чего Simulation такая структура, требующая доступ к экземплярам всех модулей. Simulation как-то расширяет имеющуюся функциональность? Делает что-то специфическое с каждым модулем? б) есть очень красивое понятие инкапсуляция, и не хотелсь бы от него отказываться.Поэтому мне показалось естественным использовать наследование
 
 Где здесь инкапсуляция? struct - это, прошу прощения за тавтологию, открытая структура, здесь ничто никуда не прячется. |  
						| 
								|  |  
								| « Последнее редактирование: 18-05-2010 08:10 от Вад » |  Записан | 
 |  |  | 
	| 
			| 
					
						| Dimka 
								ДеятельКоманда клуба    Offline 
								Пол:    | 
								|  | « Ответ #3 : 18-05-2010 10:05 »  |  | 
 
 Главной идеей системного вектора - локализация описания среды исполнения кода.  Ну пусть - компромисс между локальными и глобальными переменными. Польза: унификация инициализации модулей, сокращение числа передоваемых параметров, сокращение числа интерфейсных прозрачных функций в головных модулях, нет необходимости в многочисленных мелких изменениях при изменении среды. Вот это уже не вполне соответствует действительности. Для унификации должна быть унифицированная сущность. Например, некая структура Module, и единый код, работающий с разными экземплярами этой сущности. В противном случае вся эта "унификация" на самом деле не унификация, а набор системных функций - по отдельной функции на каждую возможную конфигурацию. Раньше я просто бы добавил эти переменные в системный вектор, но:а) в больших системах системный вектор разрастается до неприятных размеров
 б) есть очень красивое понятие инкапсуляция, и не хотелсь бы от него отказываться.
 Поэтому мне показалось естественным использовать наследование
 Про инкапсуляцию Вад  уже сказал. Остальное понятно в том смысле, что такие решения именно с такими родовыми травмами встречаются и нередко; но непонятно, зачем каково конечное назначение этих решений. Я правильно понимаю, что разные части программы нуждаются в разных подмножествах модулей из этого вектора? Но пусть даже так. Теперь вот это: Естественно при инициализации новой структуры ZSysVectorSimulation и возникла проблемы, вынесенная в заголовок данной темы. Потому что наследование используется не по назначению. Именно вариант с агрегацией (вложенной структурой) в данном случае является самым адекватным. Поскольку позволяет как раз добиться инкапсуляции. Внутри твоего нового модуля может использоваться новая структура, а вне модуля должна  использоваться только старая (которая вложенная) структура, поскольку все внешние части системы про твою новую структуру не знают и умеют работать только со старой структурой. И если всё аккуратно расписать, никогда не возникнет ошибки, про которую я написал выше - когда поля структуры-потомка при операциях копирования пропускаются, теряются, не инициализируются, и в них оказывается мусор. |  
						| 
								|  |  
								|  |  Записан | 
 
 Программировать - значит понимать (К. Нюгард)Невывернутое лучше, чем вправленное (М. Аврелий)
 Многие готовы скорее умереть, чем подумать (Б. Рассел)
 |  |  | 
	| 
			| 
					
						| Dimka 
								ДеятельКоманда клуба    Offline 
								Пол:    | 
								|  | « Ответ #4 : 18-05-2010 10:25 »  |  | 
 
 Вот пример аккуратно сделанной надстройки в C-стиле (функции и структуры, без классов), в которой старая часть системы полностью автономна и понятия не имеет о каких-либо надстройках и способах своего использования, но в то же время не спрятана внутрь обёртки. #include <cstdlib>#include <cstdio>
 
 namespace OldSystem
 {
 
 // C-style solution.
 
 struct Session
 {
 int id;
 };
 
 Session *openNewSession()
 {
 Session *session = (Session *)malloc(sizeof(Session));
 if(session != NULL)
 {
 session->id = rand();
 }
 return session;
 }
 
 void action(Session *session)
 {
 if(session != NULL)
 {
 printf("%i\n", session->id);
 }
 }
 
 void closeSession(Session *session)
 {
 if(session != NULL)
 {
 free(session);
 }
 }
 }
 
 namespace NewSystem
 {
 // C-style solution.
 
 struct Session
 {
 OldSystem::Session *base;
 int userID;
 };
 
 Session *openNewSession()
 {
 Session *session = NULL;
 OldSystem::Session *base = OldSystem::openNewSession();
 if(base != NULL)
 {
 session = (Session *)malloc(sizeof(Session));
 if(session != NULL)
 {
 session->base = base;
 session->userID = rand();
 }
 }
 return session;
 }
 
 void action(Session *session)
 {
 if(session != NULL)
 {
 OldSystem::action(session->base);
 printf("%i\n", session->userID);
 }
 }
 
 void closeSession(Session *session)
 {
 if(session != NULL)
 {
 OldSystem::closeSession(session->base);
 free(session);
 }
 }
 }
 
 int main()
 {
 NewSystem::Session *session = NewSystem::openNewSession();
 if(session != NULL)
 {
 NewSystem::action(session);
 NewSystem::closeSession(session);
 }
 return 0;
 }
 |  
						| 
								|  |  
								| « Последнее редактирование: 18-05-2010 10:30 от Dimka » |  Записан | 
 
 Программировать - значит понимать (К. Нюгард)Невывернутое лучше, чем вправленное (М. Аврелий)
 Многие готовы скорее умереть, чем подумать (Б. Рассел)
 |  |  | 
	| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | « Ответ #5 : 18-05-2010 13:02 »  |  | 
 
 Все замечания очень похожи, и это говорить скорее о Вашей правоте, чем о моей. Но.... Я сторонник ИДЕЙ ООП, но не самого ООП, особенно в исполнении С++. Поэтому аргументы типа "так принято в ООП" для меня не аргументы. Еще несколько слов об идее системного вектора.  В программе сущестует много различных сервисных модулей, например: разные системные журналы, прогрессбар окна, модуль вывода отчетов, классы данных общего пользования, доступ к базам данных. Эти модули используются в самых разных местах пограммы. Причем некоторые из них по свой природе(или даже реализации) суть синглтоны, некоторые привязаны к конкретным фреймам или классам данных. Некоторым модулям при их инициализации требуются специфические данные. Но для их использования достаточно знать только указатель на них. При этом в месте использования я не хочу ничего знать о конкретном модуле кроме этого указателя - ни как он создается, ни где находится, ни что требуется для инициализации. В этот момент они все равноправны с т.зр. использования. Теперь подробнее: инициализировать можно через список инициализации конструктора struct ZSysVectorSimulation : public ZSysVector{
 SRand*  _rand;
 SMainTree*  _tree;
 
 ZSysVectorSimulation():ZSysVector(...параметры для передачи в конструктор ZSysVector...)//,_rand(0),_tree(0)
 {
 }
 }
 
На моменте создания ZSysVectorSimulation основной ZSysVector уже существует, и мне хотелось минимальными усилиями скопировать содержимое ZSysVector в новый ZSysVectorSimulation. При этом я не хочу знать, что в ZSysVector входит - все что там есть, давай сюда. В этом случае будующие пополнения ZSysVector меня не интересуют, они будут скопированы автоматически. Поэтому данное предложение мне не подходит. Слишком много существительных    Можно поподробнее, для чего это делается? Если модули независимые - то зачем их держать в какой-то одной структуре? Если кто-то внешний управляет их временем жизни - почему бы этого кого-то не инкапсулировать в объект - менеджер модулей?Как я уже сказал, модули рождаются в разных местах и по разным поводам, и поэтому обединение процедур их создания в единую фабрику не кажется мне чем-то хорошим. Или, может быть, это все модули знают про такую структуру, и через неё обращаются к другим модулям?  Да, именно в этом идея системного вектора. В связи с первым вопросом, непонятно также, для чего Simulation такая структура, требующая доступ к экземплярам всех модулей. Simulation как-то расширяет имеющуюся функциональность? Делает что-то специфическое с каждым модулем?
  На это я вроде ответил выше. Где здесь инкапсуляция? struct - это, прошу прощения за тавтологию, открытая структура, здесь ничто никуда не прячется.
  Почему же? В С++ struct - это тот же класс, только с открытыми членами. Хотя согласен, что термин "инкапсуляция" здесь не совсем уместен. Здесь это скорее не сокрытие от других, а удобное средство суперпозиции данных. Ну пусть - компромисс между локальными и глобальными переменными.  Да, согласен. Вот это уже не вполне соответствует действительности. Для унификации должна быть унифицированная сущность. Например, некая структура Module, и единый код, работающий с разными экземплярами этой сущности. В противном случае вся эта "унификация" на самом деле не унификация, а набор системных функций - по отдельной функции на каждую возможную конфигурацию. А где сказано, что термин "унификация" применим только в интерпретации ООП?  В моем случае это унификация использования сервисных модулей. При этом никакого "набора системных функций" я тут не вижу, или я чего-то не понял, а жаль. Остальное понятно в том смысле, что такие решения именно с такими родовыми травмами встречаются и нередко; но непонятно, зачем каково конечное назначение этих решений.  Я все еще не ответил? Я правильно понимаю, что разные части программы нуждаются в разных подмножествах модулей из этого вектора?  Да. Но я не вижу, чем могут помешать лишние данные. Кроме того. если в дальнейшем потребуется обратиться к ранее неиспользованному модулю, то ни каких дополнительных телодвижений не потребуется. Потому что наследование используется не по назначению.  ПОЧЕМУ? Именно вариант с агрегацией (вложенной структурой) в данном случае является самым адекватным. Поскольку позволяет как раз добиться инкапсуляции.   Про инкапсуляцию я уже извинился, а вариант с агрегацией меня не устраивает, потому что каждый конкретный раз я должен знать откуда модуль - из ZSysVectorSimulation или из ZSysVector.  А именно этот излишек требование к знанию я и хочу избежать. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Алексей++ 
								глобальный и пушистыйГлобальный модератор    Offline 
								Сообщений: 13
								
								
								
								
								
							 | 
								|  | « Ответ #6 : 18-05-2010 14:13 »  |  | 
 
 На моменте создания ZSysVectorSimulation основной ZSysVector уже существует, и мне хотелось минимальными усилиями скопировать содержимое ZSysVector в новый ZSysVectorSimulation. При этом я не хочу знать, что в ZSysVector входит - все что там есть, давай сюда. В этом случае будующие пополнения ZSysVector меня не интересуют, они будут скопированы автоматически. Поэтому данное предложение мне не подходит.
 не понимаю. Если у тебя ZSysVectorSimulation паблик от  ZSysVector (читай: содержит в начале себя структуру  ZSysVector ), то всё равно тебе туда копировать извне. Какая разница как? Оператор ли "=", конструктор ли, memmove, чёрт побери )) Зациклился ты на мелочи, как её решить уже много способов сказано. А тебе, видишь ли, ООП нравится, но ты его не применяешь. Ты пишешь на С++ ? Ну так используй его на всю катушку! |  
						| 
								|  |  
								|  |  Записан | 
 
 |  |  | 
	| 
			| 
					
						| Dimka 
								ДеятельКоманда клуба    Offline 
								Пол:    | 
								|  | « Ответ #7 : 18-05-2010 14:39 »  |  | 
 
 Я сторонник ИДЕЙ ООП, но не самого ООП, особенно в исполнении С++. Поэтому аргументы типа "так принято в ООП" для меня не аргументы. И я сторонник идей ООП. Идея классов и логического отношения род-вид между ними описана в работе Аристотеля "Категории". Надеюсь, Аристотеля ты не подозреваешь в симпатиях к самому ООП, реализованному в C++?   В этом случае будующие пополнения ZSysVector меня не интересуют, они будут скопированы автоматически. Поэтому данное предложение мне не подходит. Ты хочешь ослабить типизацию и добиться того, чтобы во всяком месте копировалась вся структура, как бы она ни была устроена. В пределе это задача копирования произвольного по размеру куска памяти, спрятанного за void *. Для этого тебе как минимум нужно знать размер структуры. В случае присваивания типизированных переменных размер известен благодаря типу. Ты же от типа отказываешься - тогда храни размер вручную (либо в самой структуре, либо заведи идентификаторы типов и создай хэш из пар тип-размер), полиморфизм объектов в ООП в подавляющем большинстве языков реализуется именно так, только для этого задействуется RTTI, и вся работа перекладывается на компилятор. Другое дело, что ты иерархию типов переворачиваешь с ног на голову. Это всё равно, что пытаться всякое дерево назвать берёзой (поместить в переменную) и потом требовать от него берёзовых свойств. Здесь это скорее не сокрытие от других, а удобное средство суперпозиции данных. Нет, в C++ - это не "средство суперпозиции данных". Упомянутые средства имеются в других языках программирования и называются примесями - отдалённым их аналогом является импорт пространств имён. Но наследование служит для специализации, перехода по дедукции от общего к частному, а не для "подцепления вагонов к поезду". И именно поэтому у тебя оно не работает так, как тебе хочется. Здесь поможет только, как говорится, изучение матчасти - того, как именно и для чего именно так работают механизмы ООП в языке. В моем случае это унификация использования сервисных модулей. В твоём случае это называется архитектурой. Соглашением о том, что модули будут организованы так-то, и все разработчики обязаны это учитывать. Да. Но я не вижу, чем могут помешать лишние данные. Кроме того. если в дальнейшем потребуется обратиться к ранее неиспользованному модулю, то ни каких дополнительных телодвижений не потребуется. Раз они не мешают, тогда добавь их в исходную структуру - и дело с концом. Ты же весь огород городить начал именно из-за того, что они чем-то мешали. потому что каждый конкретный раз я должен знать откуда модуль - из ZSysVectorSimulation или из ZSysVector. А именно этот излишек требование к знанию я и хочу избежать.
 НЕТ! Если ты внимательно посмотришь мой пример, то OldSystem знает только про свой вариант Session, и другие варианты просто не примет. Это значит, что когда NewSession обращается наружу к OldSession, она гарантирует при вызовах передачу только вложенной структуры. А вот внутри NewSession работает со своим новым вариантом структуры. Аккуратное соблюдение типизации (если заметил, у меня нет ни одной операции приведения типов, кроме вызова malloc) является достаточным элементом контроля. Соответствие типов аргументов при вызове функции является предусловием исполнения функции, и в случае несоответствия ошибка возникнет ещё на этапе компиляции. |  
						| 
								|  |  
								| « Последнее редактирование: 18-05-2010 14:53 от Dimka » |  Записан | 
 
 Программировать - значит понимать (К. Нюгард)Невывернутое лучше, чем вправленное (М. Аврелий)
 Многие готовы скорее умереть, чем подумать (Б. Рассел)
 |  |  | 
	| 
			| 
					
						| Вад | 
								|  | « Ответ #8 : 18-05-2010 16:20 »  |  | 
 
 Слишком много существительных    Можно поподробнее, для чего это делается? Если модули независимые - то зачем их держать в какой-то одной структуре? Если кто-то внешний управляет их временем жизни - почему бы этого кого-то не инкапсулировать в объект - менеджер модулей?Как я уже сказал, модули рождаются в разных местах и по разным поводам, и поэтому обединение процедур их создания в единую фабрику не кажется мне чем-то хорошим.Если они рождаются в разных местах и по разным поводам - то к чему централизация вообще? Пытаясь централизовать данные о модулях, ты получаешь загрязнение пространства имён, потому что каждый модуль вынужден неумышленно знать про все-все другие, если ему понадобится хотя бы один из них. Я уже не говорю о том, что в случае структуры указателей ты должен откуда-то извне строго следить за правильным порядком инициализации модулей и разруливать зависимости. Я не говорю про фабрику. Коль уж модули - сингельтоны, то можно использовать фабричные методы у самих этих модулей для получения экземпляра. Тогда каждому модулю достаточно знать интерфейс нужных ему, чтобы получить указатель на существующий экземпляр и работать с ним спокойно (тут же можно реализовать и подсчёт ссылок, если потребуется). Если модуль то является сингельтоном (разделяем несколькими модулями), то нет, или просто неохота "портить шкуру" - добавлять статический фабричный метод в интерфейс - то можно сделать сингельтон-посредник, который де-факто будет иметь статический экземпляр модуля и отдавать его всем желающим. |  
						| 
								|  |  
								| « Последнее редактирование: 18-05-2010 16:23 от Вад » |  Записан | 
 |  |  | 
	| 
			| 
					
						| sss 
								Специалист    Offline | 
								|  | « Ответ #9 : 19-05-2010 01:31 »  |  | 
 
 Д'Артаньян не в силах был продолжать этот разговор, он чувствовал, что сходит с ума. Он уронил голову на руки и притворился, будто спит. ...
 |  
						| 
								|  |  
								| « Последнее редактирование: 19-05-2010 01:33 от sss » |  Записан | 
 
  while (8==8)  |  |  | 
	| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | « Ответ #10 : 19-05-2010 08:35 »  |  | 
 
 Ты хочешь ослабить типизацию и добиться того, чтобы во всяком месте копировалась вся структура, как бы она ни была устроена. В пределе это задача копирования произвольного по размеру куска памяти, спрятанного за void *. Для этого тебе как минимум нужно знать размер структуры. В случае присваивания типизированных переменных размер известен благодаря типу. Ты же от типа отказываешься ...  Где я "ослабляю типизацию"? Причем тут "void *" и "произвольный по размеру куска памяти"? От чего я отказался? У меня есть ПОЛНОСТЬЮ определенная базовая структура, у меня есть ПОЛНОСТЬЮ определенная дочерняя структура. Я всего лишь хочу перенести значение одной базовой структуры в БАЗОВУЮ же часть дочерней. В чем здесь криминал? Какие принципы ООП я нарушил? Другое дело, что ты иерархию типов переворачиваешь с ног на голову. Это всё равно, что пытаться всякое дерево назвать берёзой (поместить в переменную) и потом требовать от него берёзовых свойств.
  Где я требую от дерева березовых свойств? Вы мне приписываете что-то непонятное. У меня есть береза. Эта береза, как ни странно, является еще и деревом. Я хочу при создании объекта "береза" переписать в нее ту часть, которая касается ее как дерева. И буду в дальнейшем требовать от этой части только свойств березы как ДЕРЕВА. Что\где я здесь перевернул? Пришел товар; я о нем знаю только, что он пришел, от кого и по какой цене. В этом месте я работаю с ним как с ТОВАРОМ. Затем я узнаю, что это жидкость - мне приходится перейти к  объекту другого типа, который наследует все свойства товара плюс объем. И наконец, я узнаю, что это пиво - и полностью теряю интерес к программирования    А если серьезно, то где ошибка в только-что изложенном? Но наследование служит для специализации, перехода по дедукции от общего к частному, а не для "подцепления вагонов к поезду". И именно поэтому у тебя оно не работает так, как тебе хочется.Здесь поможет только, как говорится, изучение матчасти - того, как именно и для чего именно так работают механизмы ООП в языке. Не буду оспаривать "подцепления вагонов к поезду", хотя для меня путь от общего к частному как раз и представлялся путем последовательного уточнения свойств общего, которое чаще всего связанно с расширением списка этих свойств.  Так и моем случае: есть самая общая среда выполнения программа, а отдельные блоки могут иметь свою среду, которая обладает только ей присущими свойствами, при этом общая среда программы никуда не делась.  Где я здесь пошел против "матчасти"? Какие "механизмы ООП" и не должны обслуживать мои потребности? В моем случае это унификация использования сервисных модулей. В твоём случае это называется архитектурой. Соглашением о том, что модули будут организованы так-то, и все разработчики обязаны это учитывать. И чем это плохо? А в Ваших системах это не так? Да. Но я не вижу, чем могут помешать лишние данные. Кроме того. если в дальнейшем потребуется обратиться к ранее неиспользованному модулю, то ни каких дополнительных телодвижений не потребуется. Раз они не мешают, тогда добавь их в исходную структуру - и дело с концом. Ты же весь огород городить начал именно из-за того, что они чем-то мешали. Но в других частях программы они просто не имеют смысла. Так можно договориться до: наследование нам нужно только ради полиморфизма и, если такого-го нет - вали все в кучу. Я надеюсь, Вы не это имели ввиду? потому что каждый конкретный раз я должен знать откуда модуль - из ZSysVectorSimulation или из ZSysVector. А именно этот излишек требование к знанию я и хочу избежать.
 НЕТ! Если ты внимательно посмотришь мой пример, то OldSystem знает .......Прекрасно!  Но часто ли Вы используете данный стиль в своих последних разработках? Мне же надо тоже самое только в С++. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Dale | 
								|  | « Ответ #11 : 19-05-2010 09:11 »  |  | 
 
 Так можно договориться до: наследование нам нужно только ради полиморфизма и, если такого-го нет - вали все в кучу. Я надеюсь, Вы не это имели ввиду?ezus
 , можно вопрос по ходу дискуссии? Мне по небольшому опыту программирования на C++ известны два основных применения наследования: Наследование интерфейса (открытое наследование от класса-предка). Потомок берется выполнять все обязательства базового класса, так что клиент в идеале не должен обнаружить различия.Наследование реализации (закрытое наследование от класса-предка). Потомок представляется как некая новая сущность с собственным поведением, пользуясь для своих нужд средствами предка.
 Какой из двух вариантов предполагалось использовать в данной задаче? Или, возможно, какой-то третий вариант, который я не учел? P.S. Прошу прощения, если об этом уже говорилось выше. Тема интересная, но диалог ведется параллельно в слишком многих плоскостях - от античной философии до хакерства на уровне "а что там на самом деле творится в стеке?". В условиях ограниченного времени трудно проследить основную нить, стек переполняется. |  
						| 
								|  |  
								|  |  Записан | 
 
 Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.
 Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard
 
 Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
 |  |  | 
	| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | « Ответ #12 : 19-05-2010 09:17 »  |  | 
 
 Если они рождаются в разных местах и по разным поводам - то к чему централизация вообще?   А как мне в произвольном месте программы иметь возможность использовать произвольный сервисный модуль? Мне известны всего несколько способов(не по важности или глупости, а только как пришли в голову): 1. Глобальные переменные - в настоящий момент преданные жуткой анафеме, хотя я их и не считаю, таким уж ЖУТКИМ грехом. 2. Синглтоны - классная вещь, на мой взгляд один из лучших неочевидных паттернов. Но далеко не все модули могут быть так оформлены. 3. Передаваемые параметры - в функции или при инициализации. Во-первых их может быть много, что неудобно. Во- вторых: передавать все - глупо, а иначе любая новая потребность порождает изменения по все цепочке вызовов. 4. Цепочки сред. При создании объекта ему передается обратный указатель на создателя. Так двигаясь в обратном порядке можно добраться до любого модуля в программе (конечно, если весь путь public). Требует знание структуры всей программы. 5. Локальный. Каждый модуль сам себе порождает сервисные модули. К сожалению не все модули можно так породить.  6. Сервисный центр. Некий модуль, который предоставляет доступ к сервисным модулям. Хорош для на общесистемном уровне. Но трудно покрывает локальные среды. 7. Системный вектор - чем-то схож с Сервисным центром, но более прост и мобилен. О недостатках не говорю, т.к. они подробно уже разобраны не мной. Наверняка есть еще что-нибудь. Буду рад пополнить свою коллекцию.  Пытаясь централизовать данные о модулях, ты получаешь загрязнение пространства имён, потому что каждый модуль вынужден неумышленно знать про все-все другие, если ему понадобится хотя бы один из них. Я уже не говорю о том, что в случае структуры указателей ты должен откуда-то извне строго следить за правильным порядком инициализации модулей и разруливать зависимости.  Согласен. Но нет идеальных решений. Хотя в С++, к счастью - слава создателям   ) в данном случае "ЗНАТЬ" слишком сильно сказано - я знаю только указатель на какой-то там модуль и больше ничего. И если он мне не интересен, то описание   не связывает их даже на этапе компиляции.   Коль уж модули - сингельтоны,   Далеко не все.  Тогда каждому модулю достаточно знать интерфейс нужных ему, чтобы получить указатель на существующий экземпляр и работать с ним спокойно .
   А в моем случае и этого знать не надо. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | « Ответ #13 : 19-05-2010 09:18 »  |  | 
 
 Д'Артаньян не в силах был продолжать этот разговор, он чувствовал, что сходит с ума. Он уронил голову на руки и притворился, будто спит. ...
Сочувствую. Я и сам себя так ощущаю. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | « Ответ #14 :  19-05-2010 09:42 »   |  | 
 
 Мне по небольшому опыту программирования на C++ известны два основных применения наследования: Наследование интерфейса (открытое наследование от класса-предка). Потомок берется выполнять все обязательства базового класса, так что клиент в идеале не должен обнаружить различия.Наследование реализации (закрытое наследование от класса-предка). Потомок представляется как некая новая сущность с собственным поведением, пользуясь для своих нужд средствами предка.
Мне кажется здесь 2 ортогональных измерения: одно - интерфейс \ реализация другой - функциональность. Во втором случае возможно как усечение возможностей - базовый класс используется только для своих нужд, так и расширение возможностей - предоставление клиенту кроме базовых еще и дополнительные возможности. Пример: цепочка наследования в окнах MFC Какой из двух вариантов предполагалось использовать в данной задаче? Или, возможно, какой-то третий вариант, который я не учел?
  В данном случае предполагается второе уточнение второго варианта. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Вад | 
								|  | « Ответ #15 : 19-05-2010 09:42 »  |  | 
 
 2. Синглтоны - классная вещь, на мой взгляд один из лучших неочевидных паттернов. Но далеко не все модули могут быть так оформлены.
 Не все, но многие? Кроме того, я говорил о сингельтоне-посреднике как частном решении. Если же модуль используется в нескольких экземплярах - твой вариант со структурой тоже не подходит.  Пытаясь централизовать данные о модулях, ты получаешь загрязнение пространства имён, потому что каждый модуль вынужден неумышленно знать про все-все другие, если ему понадобится хотя бы один из них. Я уже не говорю о том, что в случае структуры указателей ты должен откуда-то извне строго следить за правильным порядком инициализации модулей и разруливать зависимости.  Согласен. Но нет идеальных решений. Хотя в С++, к счастью - слава создателям   ) в данном случае "ЗНАТЬ" слишком сильно сказано - я знаю только указатель на какой-то там модуль и больше ничего. И если он мне не интересен, то описание   не связывает их даже на этапе компиляции.Ну да. Под "знать" я подразумевал, что все модули знают о единой структуре ("векторе"), вынуждены её поддерживать и имеют в своём пространстве имён набор деклараций типов.  Тогда каждому модулю достаточно знать интерфейс нужных ему, чтобы получить указатель на существующий экземпляр и работать с ним спокойно .
   А в моем случае и этого знать не надо.А чтобы работать с модулем - тоже не надо интерфейс?   |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | « Ответ #16 : 19-05-2010 09:55 »  |  | 
 
 Если же модуль используется в нескольких экземплярах - твой вариант со структурой тоже не подходит.  Почему не подходит? В среде каждого конкретного модуля должен быть только один экземпляр, но самих модулей может быть несколько. Пример: есть 2-3-4 окна, в каждом из которых мы работаем со своей базой данных. Ну да. Под "знать" я подразумевал, что все модули знают о единой структуре ("векторе"), вынуждены её поддерживать и имеют в своём пространстве имён набор деклараций типов. Да - о векторе и НЕТ о типах. Надо поддерживать только типы используемых модулей, а это надо делать в любом случае. А если модуль не используется, то необходим единственный стандартный тип void*    Тогда каждому модулю достаточно знать интерфейс нужных ему, чтобы получить указатель на существующий экземпляр и работать с ним спокойно .   А в моем случае и этого знать не надо. А чтобы работать с модулем - тоже не надо интерфейс?    Надо, но только для использования, а не порождения.  Хотя Вы правы - это не такое уж принципиальное отличие.  |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Dale | 
								|  | « Ответ #17 : 19-05-2010 11:03 »  |  | 
 
 Мне кажется здесь 2 ортогональных измерения:одно - интерфейс \ реализация
 другой - функциональность.
 Во втором случае возможно как усечение возможностей - базовый класс используется только для своих нужд, так и расширение возможностей - предоставление клиенту кроме базовых еще и дополнительные возможности.
 ...
 В данном случае предполагается второе уточнение второго варианта.
 
 Спасибо. Если я правильно понял конструкцию, потомок должен уметь все то же самое, что и предок, и плюс кое-что дополнительно? В таком случае структурой в стиле старого доброго C мы вряд ли обойдемся. Придется писать на C++, а значит, определять копирующий конструктор и/или операторы присваивания/преобразования типов и т.п. Пример: цепочка наследования в окнах MFC Если мы говорим о хорошем стиле ООП (а я надеюсь, что это так и есть), нам лучше все же не брать MFC в качестве образца для подражания. |  
						| 
								|  |  
								|  |  Записан | 
 
 Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.
 Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard
 
 Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
 |  |  | 
	| 
			| 
					
						| Dimka 
								ДеятельКоманда клуба    Offline 
								Пол:    | 
								|  | « Ответ #18 : 19-05-2010 11:37 »  |  | 
 
 Где я "ослабляю типизацию"? Причем тут "void *" и "произвольный по размеру куска памяти"? От чего я отказался?У меня есть ПОЛНОСТЬЮ определенная базовая структура, у меня есть ПОЛНОСТЬЮ определенная дочерняя структура. Я всего лишь хочу перенести значение одной базовой структуры в БАЗОВУЮ же часть дочерней. В чем здесь криминал? Какие принципы ООП я нарушил?
 У тебя нет "базовой части" переменной  - вот в этом и криминал. "Базовая часть" бывает только у значения . Поэтому я и говорил, что введение вложенной структуры может решить твои затруднения. А ослабление типизации происходит в том случае, когда из значения  производного типа извлекают "базовую часть". К операции записи в переменную это не относится. Но ты это попытался скомбинировать, что показывает непонимание "матчасти". Теперь же я поясню, как твои действия оцениваются со стороны матчасти. Ты берёшь переменную дочернего типа и пробуешь записать туда значение базового типа. Комплиятор не знает, как правильно расширить значение базового типа до размера переменной. С его точки зрения под видом берёзы ему пытаются всучить чёрт знает что, может тополь какой. Где я требую от дерева березовых свойств? Вы мне приписываете что-то непонятное. У меня есть береза. Эта береза, как ни странно, является еще и деревом. Я хочу при создании объекта "береза" переписать в нее ту часть, которая касается ее как дерева. И буду в дальнейшем требовать от этой части только свойств березы как ДЕРЕВА. Что\где я здесь перевернул?Пришел товар; я о нем знаю только, что он пришел, от кого и по какой цене. В этом месте я работаю с ним как с ТОВАРОМ. Затем я узнаю, что это жидкость - мне приходится перейти к  объекту другого типа, который наследует все свойства товара плюс объем. И наконец, я узнаю, что это пиво - и полностью теряю интерес к программирования  А если серьезно, то где ошибка в только-что изложенном?
 Здесь ошибки нет, но это не соответствует тому, что у тебя написано в коде. Выражение (A)b = a; означает, что у тебя есть дерево (а не берёза) - значение в переменной a. И ты хочешь это дерево засунуть в берёзу - переменную b. То, что ты берёзу собираешься использовать лишь как дерево, компилятор не знает - не телепат, поэтому он старается обеспечить наличие в переменной b значения типа B (именно берёзы, а не чего-то там другого). Приведение к A - это уже из области "ловкости рук" при впаривании просроченной тухлятины под видом свежего товара. Если тебе нужны только свойства дерева, тогда и объявляй переменную типа A. Наверно ты бы тоже обиделся и не согласился, если тебе под видом пива пытались бы всучить квас.   И чем это плохо? А в Ваших системах это не так? Ничем. Я просто уточнил, что речь идёт не об унификации, а об архитектуре. Но в других частях программы они просто не имеют смысла. Так можно договориться до: наследование нам нужно только ради полиморфизма и, если такого-го нет - вали все в кучу. Я надеюсь, Вы не это имели ввиду? Самое главное - это инкапсуляция и достигаемая с её помощью возможность абстрагироваться от несущественных деталей. Без неё обсуждение наследования и полиморфизма смысла не имеет. По крайней мере у тебя в наследовании никакого смысла нет. Поскольку на самом деле наследование (inheritance) наряду с делегированием с прототипами - это лишь языковые реализации чисто логического отношения обобщения (generalization), только первое используется в типизированных языках, а второе в нетипизированных, лично я могу добиться полиморфизма и без наследования, если мне так приспичит   Прекрасно! Но часто ли Вы используете данный стиль в своих последних разработках?
 Мне же надо тоже самое только в С++.
 В C++ нужно структуру "растянуть" на всё пространство имён и включить в неё функции, убрав их параметр типа структуры. Это всё. Принципы ООП не зависят от языка реализации. Я могу и на чистом C реализовать и объекты, и их инкапсуляцию, и наследование с полиморфизмом, и без всяких издевательств над компилятором. Когда хорошо понимаешь принципы и возможные способы их "физической" реализации, язык программирования значения не имеет. |  
						| 
								|  |  
								|  |  Записан | 
 
 Программировать - значит понимать (К. Нюгард)Невывернутое лучше, чем вправленное (М. Аврелий)
 Многие готовы скорее умереть, чем подумать (Б. Рассел)
 |  |  | 
	| 
			| 
					
						| Dimka 
								ДеятельКоманда клуба    Offline 
								Пол:    | 
								|  | « Ответ #19 : 19-05-2010 11:40 »  |  | 
 
 Придется писать на C++, а значит, определять копирующий конструктор и/или операторы присваивания/преобразования типов и т.п. Эта здравая мысль звучала и раньше, но почему-то не нравится зачинщику обсуждения. Если мы говорим о хорошем стиле ООП (а я надеюсь, что это так и есть), нам лучше все же не брать MFC в качестве образца для подражания. Категорически согласен   |  
						| 
								|  |  
								|  |  Записан | 
 
 Программировать - значит понимать (К. Нюгард)Невывернутое лучше, чем вправленное (М. Аврелий)
 Многие готовы скорее умереть, чем подумать (Б. Рассел)
 |  |  | 
	| 
			| 
					
						| Антон (LogRus) | 
								|  | « Ответ #20 : 19-05-2010 11:46 »  |  | 
 
 Здесь ошибки нет, но это не соответствует тому, что у тебя написано в коде. Выражение (A)b = a; означает, что у тебя есть дерево (а не берёза) - значение в переменной a. И ты хочешь это дерево засунуть в берёзу - переменную b. То, что ты берёзу собираешься использовать лишь как дерево, компилятор не знает - не телепат, поэтому он старается обеспечить наличие в переменной b значения типа B (именно берёзы, а не чего-то там другого). Приведение к A - это уже из области "ловкости рук" при впаривании просроченной тухлятины под видом свежего товара. Если тебе нужны только свойства дерева, тогда и объявляй переменную типа A.я бы рассматривал эту строку иначе (если убрать из неё изначальную ошибку) void DataCopy(A * dst, A * src){
 *dst = *src;
 }
 .............
 B b; // B унаследован от A
 A a;
 DataCopy(&b, &a);
 
Думаю тут все согласятся, что подобное приведение является верным и вполне себе классическим примером использования наследования, т.е. я не пытаюсь базовый тип привести к наследнику, а наследника привожу к базовому. единственное, что автор темы совершил небольшую опечатку, которая привела к созданию временного объекта, но идея от этого не изменилась. |  
						| 
								|  |  
								|  |  Записан | 
 
 Странно всё это.... |  |  | 
	| 
			| 
					
						| Dimka 
								ДеятельКоманда клуба    Offline 
								Пол:    | 
								|  | « Ответ #21 : 19-05-2010 12:11 »  |  | 
 
 LogRus, вот поэтому ему и говорили написать оператор присваивания или копирующий конструктор. Но не хочет. |  
						| 
								|  |  
								|  |  Записан | 
 
 Программировать - значит понимать (К. Нюгард)Невывернутое лучше, чем вправленное (М. Аврелий)
 Многие готовы скорее умереть, чем подумать (Б. Рассел)
 |  |  | 
	| 
			| 
					
						| Антон (LogRus) | 
								|  | « Ответ #22 : 19-05-2010 12:14 »  |  | 
 
 Dimka, копирующий конструктор это конечно круто, НО это некоим образом не объясняет почему приведённая конструкция не работает |  
						| 
								|  |  
								|  |  Записан | 
 
 Странно всё это.... |  |  | 
	| 
			| 
					
						| Dale | 
								|  | « Ответ #23 : 19-05-2010 12:28 »  |  | 
 
 (A)b = a; ... Приведение к A - это уже из области "ловкости рук" при впаривании просроченной тухлятины под видом свежего товара.И в довершение картины - ловкий продавец при этом еще и старается говорить не на языке покупателя, чтобы еще больше запутать. Поскольку процитированный фрагмент написан не  на C++. |  
						| 
								|  |  
								|  |  Записан | 
 
 Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.
 Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard
 
 Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
 |  |  | 
	| 
			| 
					
						| Dimka 
								ДеятельКоманда клуба    Offline 
								Пол:    | 
								|  | « Ответ #24 : 19-05-2010 13:11 »  |  | 
 
 LogRus, твоя конструкция тоже не объясняет, почему не работает его выражение. Скорее наоборот, твой работающий пример способен навести на мысль, что и его конструкция должна работать. |  
						| 
								|  |  
								|  |  Записан | 
 
 Программировать - значит понимать (К. Нюгард)Невывернутое лучше, чем вправленное (М. Аврелий)
 Многие готовы скорее умереть, чем подумать (Б. Рассел)
 |  |  | 
	| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | « Ответ #25 : 20-05-2010 06:23 »  |  | 
 
 Прошу прощения у LogRus . Конструкция   прекрасно работает.  Огромное спасибо. Ты вернул мне веру в себя, в ООП, и даже в С++. Конструкция (А)b=a; не работает по чисто компиляторским причинам. Например обратные  a=(A)b;  и даже a=b; прекрасно хаваются и исполняются. Правда, побочные последствия я не исследовал, возможно они и существуют, а будет жаль. |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | « Ответ #26 : 20-05-2010 06:26 »  |  | 
 
 LogRus, вот поэтому ему и говорили написать оператор присваивания или копирующий конструктор. Но не хочет.
 Вы можете привести мне примеры предложенных приемов без использования поэлементного присвоения? |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| Алексей++ 
								глобальный и пушистыйГлобальный модератор    Offline 
								Сообщений: 13
								
								
								
								
								
							 | 
								|  | « Ответ #27 : 20-05-2010 06:34 »  |  | 
 
 ezus,  Конструкция (А)b=a; не работает по чисто компиляторским причинам. Например обратные  a=(A)b;  и даже a=b; прекрасно хаваются и исполняются. Правда, побочные последствия я не исследовал, возможно они и существуют, а будет жаль.
 1)(А)b=a; -  можно расписать так: A& ra=A();ra=b;
 ra=a;
 //присвоения b=... нету.
 
2)a=(A)b;  -  можно расписать так: 3) a=b; - вызывается копирование по умолчанию , то есть так: |  
						| 
								|  |  
								|  |  Записан | 
 
 |  |  | 
	| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | « Ответ #28 : 20-05-2010 07:04 »  |  | 
 
 (A)b = a; ... Приведение к A - это уже из области "ловкости рук" при впаривании просроченной тухлятины под видом свежего товара.И в довершение картины - ловкий продавец при этом еще и старается говорить не на языке покупателя, чтобы еще больше запутать. Поскольку процитированный фрагмент написан не  на C++.На "ловкость рук" и "просроченную тухлятину" я не ответил в свое время во-первых у меня не было работающего примера, а во-вторых на грубость, тем более необоснованную, вообще не отвечаю. Но сейчас я убедился в своей правоте и попробую спокойно отреагировать на приведенную цитату. Чем  не C++? Только тем, что Вы его не используете?(Пардон не удержался    )С точки зрения языка: Моя ошибка заключалась в незнании особенностей КОНКРЕТНОГО компилятора и в наследии пресловутой Java. Компилятор получил всю необходимую информацию для исполнения того, что я хочу. Я просто не знал о временной переменной. В Java конструкция (A) не оператор как здесь, а только указание интерпретатору на приведении типов. Поэтому работает (A&)b = a;. Хотя и конструкция (A)b тоже имеет смысл. Например: ((A)b).ff(); для доступа к функции ff класса А.С точки зрения ООП или "тухлятины": Разве береза перестала быть деревом только потому что она береза? Разве ее нельзя рассматривать как дерево? Что в конструкции "(A&)b = a" я впариваю как тухлятиню? Что я здесь продаю на другом языке? |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	| 
			| 
					
						| ezus 
								Опытный    Offline | 
								|  | « Ответ #29 : 20-05-2010 07:09 »  |  | 
 
 Алексей1153++Спасибо. Полезно.
 А о своей ошибке я уже сказал в предыдущем посте.
 |  
						| 
								|  |  
								|  |  Записан | 
 |  |  | 
	|  |