Перейти к содержанию
    

Доступ к методам (функциям) класса.

...

Спасибо. Согласен... Наследование для решения вопроса доступа - это, как минимум, не экономно.

...

Если говорить по теме, то вопрос доступа решил так, как рекомендовал dxp.

А именно:

- каждый объект "размещен" в своей "единице компиляции" (ЕдКомп) /связка файлов, например, One.h и One.cpp/ (так делал и раньше);

- член класса /TOne mTOne;/ объявляется теперь в TOne.cpp, а не в main.cpp как раньше;

- а для возможности использования функций (свойств) TOne, там где нужно, mTOne объявляется с квалификатором extern.

 

... файл One.h

...
class TOne
{
...
public:
void FuncOne(void);
...
}

... файл One.cpp

...
TOne mTOne;
....
void FuncOne(void)
{
}

... файл Two.h

...
class TTwo
{
...
public:
void FuncTwo(void);
...
}

... файл Two.cpp

....
#include "One.h"

TTwo mTTwo;
extern TOne mTOne;
....
void FuncTwo(void)
{
...
mTOne.FuncOne();
...
}

На мой взгляд, наиболее простой и понятный способ.

 

А если говорить не совсем по теме, то главная проблема ("разруха" в первоисточнике) "...в головах, а не в клозетах..." (с) Нельзя заниматься ООП оперируя процедурами. Как только приходишь к пониманию "объекта", так все очень упрощается. И "создание" (представление) модели программы, и понимание взаимоотношений между разными частями этой программы. Не буду пересказывать написанное Гради Бучем (и про мыслительный процесс человека в повседневной жизни, и про количество абстракций, с которыми человек способен работать одновременно, и т.д.), но, на мой взгляд, достаточно прочесть главу "1.2. Структура сложных систем" и вопросов о полезности и естественности перехода к ООП не остается. Мир вокруг нас состоит из Объектов, а не из Процедур. И живем мы, используя Объекты и их какие-то свойсва (Процедуры), а не Процедуры каких-то Объектов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поиск "оптимального" решения продолжается....

Позволю себе напомнить "проблему"...

Есть некий объект (класс), допустим, модуль отладки (DBGU) через UART. Естественно, объект может (и должен) инициализироваться, принимать и передавать инфу, и др.

Свойства (функции, методы....) этого объекта (класса), ес-но должны быть доступны другим, если не сказать всем остальным, объектам. При этом, все остальные обекты могут находиться в "родительско-наследственных" отношениях. Как получить доступ к функциям DBGU?

 

То, что использовать наследование DBGU для получения доступа к его свойсвам, не рационально описано выше в теме. Содание класса TDBGU как виртуального гарантирует, что при косвенном ("вложенном") наследовании "верхних" классов не будет создано двух копий TDBGU. А если virtual TDBGU наследуется двумя "отдельными" (разными, самостоятельными...) классами, то для каждого будет создана своя копия TDBGU... Или я не прав? И что вообще означает "производный класс содержит копию базового класса"? Про "создание" копий переменных базового класса при наследовании понятно, а как наследуются функции? Просмотр "откомпилированного" кода и здравый смысл подсказывает, что функции наследуются "беззатратно". Но если это так, то при условии остутсвия переменных в базовом классе его копия ничего не "весит"?

 

Объявление глобального объекта mDBGU с использованием там "где нужно" конструкции extern ... mDBGU так же не лишено недостатков (как способ). Во-первых, глобальный объект "размещается" в "глобальной" области, при этом размещается "раз и навегда". Ну и, во-вторых, нарушается один из первейших принципов ООП - инкапсуляция. (Второе, конечно, относится скорее к стилю программирования нежели к "ошибкам", но если не выполнять правил, то зачем переходить на CPP?). Кстати, натолкнуло на эту мысль упорное "нежелание" IAR инициализировать глобальные объекты при startup'e.

 

Далее... friend... ЧтО позволяет "делать" функция или класс , объявленные в другом как friend? Использовать функции (свойства) friend класса? НО! только через инициализированный объект mDBGU! А как быть если этот объект еще не "создан" или не "виден"?.... (И вообще, насколько я понял, конструкция friend "нужна" для доступа к privat переменным friend класса.)

 

Остается доступ к свойствам (функциям) DBGU через их объявление с квалиф. static. Ранее я писал, что "...static гарантирует, что будет создана только одна копия этого элемента...". Это справедливо, но только для переменных (может я ошибаюсь?). Для функций же это квалификатор "устанавливает" глобальную "видимость" и "доступность" даже до инициализации объекта mDBGU..и только.

 

Таким образом, объявив ф-цию как в staticах, можно ее "использовать" где угодно, конечно предварительно "показав" ее (класс) через #include. Это очень "смахивает" на Си-подход, но лучшего в голову не приходит... Хотя спинной мозг "подсказывает", что не все так однозначно... :)

 

Просвятите, плз.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поиск "оптимального" решения продолжается....

Позволю себе напомнить "проблему"...

Есть некий объект (класс), допустим, модуль отладки (DBGU) через UART. Естественно, объект может (и должен) инициализироваться, принимать и передавать инфу, и др.

Свойства (функции, методы....) этого объекта (класса), ес-но должны быть доступны другим, если не сказать всем остальным, объектам. При этом, все остальные обекты могут находиться в "родительско-наследственных" отношениях. Как получить доступ к функциям DBGU?

Я так понимаю различные классы будут обращаться к DBGU - т.о. надо сделать систему доступа к общему ресурсу или создать список заданий для DBGU

То, что использовать наследование DBGU для получения доступа к его свойсвам, не рационально описано выше в теме. Содание класса TDBGU как виртуального гарантирует, что при косвенном ("вложенном") наследовании "верхних" классов не будет создано двух копий TDBGU. А если virtual TDBGU наследуется двумя "отдельными" (разными, самостоятельными...) классами, то для каждого будет создана своя копия TDBGU... Или я не прав? И что вообще означает "производный класс содержит копию базового класса"? Про "создание" копий переменных базового класса при наследовании понятно, а как наследуются функции? Просмотр "откомпилированного" кода и здравый смысл подсказывает, что функции наследуются "беззатратно". Но если это так, то при условии остутсвия переменных в базовом классе его копия ничего не "весит"?

Мне кажется, что вы неправильно трактуете суть полиморфизма - советую прочитать Дж.Либерти - Освой С++ за 21 день. Попробую автора процитировать:

Можно объвить множество окон разных типов, включая диалоговые, прокручиваемые и поля списков и т.д.), после чего создавать их в программе с помощью единственного виртуального метода draw(). Создав указатель на базовое окно и присваивая этому указателю адреса объектов производных классов, можно обращаться к методу draw() независимо от того, с каким из объектов в данный момент связан указатель. Причем всегда будет вызываться вариант метода, специфичный для класса выбранного объекта.

Объявление глобального объекта mDBGU с использованием там "где нужно" конструкции extern ... mDBGU так же не лишено недостатков (как способ). Во-первых, глобальный объект "размещается" в "глобальной" области, при этом размещается "раз и навегда".

А как по вашему работают cin и cout - ведь вы ведь нигде от них не наследуетесь, а они отвечают за ввод и вывод информации

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

Ничего не нарушается.

Кстати, натолкнуло на эту мысль упорное "нежелание" IAR инициализировать глобальные объекты при startup'e.

До входа в main() все глобальные классы проинициализированы, т.е. вызваны все конструкторы.

 

Далее... friend... ЧтО позволяет "делать" функция или класс , объявленные в другом как friend? Использовать функции (свойства) friend класса? НО! только через инициализированный объект mDBGU! А как быть если этот объект еще не "создан" или не "виден"?.... (И вообще, насколько я понял, конструкция friend "нужна" для доступа к privat переменным friend класса.)

Вообще никогда friend-ом не пользуюсь

Остается доступ к свойствам (функциям) DBGU через их объявление с квалиф. static. Ранее я писал, что "...static гарантирует, что будет создана только одна копия этого элемента...". Это справедливо, но только для переменных (может я ошибаюсь?). Для функций же это квалификатор "устанавливает" глобальную "видимость" и "доступность" даже до инициализации объекта mDBGU..и только.

Таким образом, объявив ф-цию как в staticах, можно ее "использовать" где угодно, конечно предварительно "показав" ее (класс) через #include. Это очень "смахивает" на Си-подход, но лучшего в голову не приходит... Хотя спинной мозг "подсказывает", что не все так однозначно... :)

Static переменные и методы нужны для действий, которые оперируют общими ресурсами для всех экземпляров класса.

Также можно создавать только static классы - что я очень часто и использую - на производительность это никак не сказывается, т.к. компилятор все это сводит к С-реализации

Изменено пользователем bookevg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я так понимаю различные классы будут обращаться к DBGU - т.о. надо сделать систему доступа к общему ресурсу или создать список заданий для DBGU

Совершенно верно. Только ЧТО делать понятно, а вот что на счет того КАК делать?

 

Мне кажется, что вы неправильно трактуете суть полиморфизма - советую прочитать Дж.Либерти - Освой С++ за 21 день. Попробую автора процитировать:

Можно объвить множество окон разных типов, включая диалоговые, прокручиваемые и поля списков и т.д.), после чего создавать их в программе с помощью единственного виртуального метода draw(). Создав указатель на базовое окно и присваивая этому указателю адреса объектов производных классов, можно обращаться к методу draw() независимо от того, с каким из объектов в данный момент связан указатель. Причем всегда будет вызываться вариант метода, специфичный для класса выбранного объекта.

Не соглашусь с Вами. В приведенной цитате речь идет о методах, т.е. виртуальных функциях . Которые естественно должны иметь свое "наполнение" в производных классах. Но я рассуждал не о наследовании виртуальных функций и полиформизме, а о наследовании virtual класса класса как virtual. Как я понимаю, виртуальный класс вовсе не "обязан" содержать виртуальные функции. Более того, виртульные функции можно создавать только в абстактнх классах. То есть объектов таких классов создано быть не может. Просто "не получится". Не так ли?

 

А как по вашему работают cin и cout - ведь вы ведь нигде от них не наследуетесь, а они отвечают за ввод и вывод информации

Извините, не понял к чему это... Я имел ввиду нецелесообразность обявления global объектов, если есть возможность сделать их локальными.

 

Ничего не нарушается.

Спорно. Если объект объявляется/выносится за пределы своего "ареала обитания", то разве это соответсвует принципу encapsulation? Зачем в список фруктовых деревьев сада вносить функцию измерения размера косточки плода? Ведь к этой функции все равно придется обращаться, как минимум, через Плод. (На точности примера не настаиваю)

 

До входа в main() все глобальные классы проинициализированы, т.е. вызваны все конструкторы.

Так должно быть, но не всегда так есть. "Встроенный" Cstartup.s79 для IARARM НЕ СОДЕРЖИТ вызовов конструкторов до входа в main(). На форуме упомянуты способы введения такой инициализации (в частности, Сергеем Борщем).

 

Вообще никогда friend-ом не пользуюсь

А почему?

 

Static переменные и методы нужны для действий, которые оперируют общими ресурсами для всех экземпляров класса.

Т.е. данными?

 

Также можно создавать только static классы - что я очень часто и использую - на производительность это никак не сказывается, т.к. компилятор все это сводит к С-реализации

Другими словами, если не смог (к Вам никакого отношения не имеет!) написать на С++ используй static и пиши как на С... :05:

...

И все-таки, если класс не содержит ресурсов (переменных, данных), то будет ли формироваться избыточный код при его "многократном" наследовании?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Совершенно верно. Только ЧТО делать понятно, а вот что на счет того КАК делать?

Не соглашусь с Вами. В приведенной цитате речь идет о методах, т.е. виртуальных функциях . Которые естественно должны иметь свое "наполнение" в производных классах. Но я рассуждал не о наследовании виртуальных функций и полиформизме, а о наследовании virtual класса класса как virtual.

Зачем нужно, чтобы весь класс был виртуальным - надо только, чтобы методы были и этого достаточно

Как я понимаю, виртуальный класс вовсе не "обязан" содержать виртуальные функции.

А зачем тогда он нужен - виртуальность нужна как раз только для методов

Более того, виртульные функции можно создавать только в абстактнх классах. То есть объектов таких классов создано быть не может. Просто "не получится". Не так ли?

Еще как можно использовать - т.е. объявил класс СTimers1 как обычно, создал вирт.метод СTimers1::Update, затем породил класс СTimers2 от СTimers1 и переписал СTimers1::Update и создал два экземляра СTimers1 Timers1; и СTimers2 Timers2 и работаешь с ними.

Извините, не понял к чему это... Я имел ввиду нецелесообразность обявления global объектов, если есть возможность сделать их локальными.

Я как понял вашу задачу: вы хотите, чтобы другие объекты через экземпляр класса DBGU общались с внешним миром - но ведь на PC-машинах все наши объекты общаются с внешним миром или через cin/cout или GUI

Спорно. Если объект объявляется/выносится за пределы своего "ареала обитания", то разве это соответсвует принципу encapsulation? Зачем в список фруктовых деревьев сада вносить функцию измерения размера косточки плода? Ведь к этой функции все равно придется обращаться, как минимум, через Плод. (На точности примера не настаиваю)

А зачем почти что всем животным воздух?

Так должно быть, но не всегда так есть. "Встроенный" Cstartup.s79 для IARARM НЕ СОДЕРЖИТ вызовов конструкторов до входа в main(). На форуме упомянуты способы введения такой инициализации (в частности, Сергеем Борщем).

Незамечал, я обычно всегда делаю свой Cstartup.s79, так что мог и ошибиться, но вроде еще с AVR глобаль.объекты инициализировал компилятор - даже пришлось убрать инициализацию из конструкторов, чтоб все происходило по порядку определяемому мною

А почему?

Пока нужды не возникало использование friend, если и порывался. то находил другие пути выхода из ситуации, да и не все embedded компиляторы поддерживали это лет 4-6 назад.

Т.е. данными?

Необязательно, м.б. и общие действия, но в принципе все равно все сводится к данным

Другими словами, если не смог (к Вам никакого отношения не имеет!) написать на С++ используй static и пиши как на С... :05:

Все проекты разрабатываются в Microsoft Visual Studio - при оформлении классом одновременно улучшается читабельность кода

...

И все-таки, если класс не содержит ресурсов (переменных, данных), то будет ли формироваться избыточный код при его "многократном" наследовании?

Зависит от умности компилятора и линкера.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ув. bookevg, Вы, безусловно, во многом правы! :) (Мне кажется, что понятия виртуальный класс не существует вовсе. Абстрактный, т.е. не имеющий объектов/членов - да, такое понятие/механизм описан. Другое дело, если отнаследовать любой класс как virtual, для исключения создания более одной копии при косвенном наследовании, - пожалуйста. Да и к полиформизму наследование класса с идентификатором virtual отношения не имеет. Поправьте, если ошибаюсь.)

 

Спасибо Вам за участие. :a14:

Свой выбор "оптимального" способа я сделал. :) Правда, только благодаря помощи dxp. :)

Не буду коверкать "первоисточник" - завтра вставлю цитатой... Смысл сказанного прост до банальности. :)

 

С++ - язык гибридный, он сочетает разные стили программирования - и процедурный (как в голом С), и объектный (на классах), и ООП (на иерархиях классов с полиморфизмом). Можно использовать любые средства в любых сочетаниях - какие на задачу ложатся, те и использовать. Не нужно пытаться применять ООП там, где все легко и управляемо делается процедурным способом. Но там, где ООП к месту, его надо применять, не колеблясь. :)

 

Зачем из пушки стрелять по воробьям? Если, как в моем случае, из высокоуровнего класса вызывается функция низкоуровнего кл., при этом вызываемая функция просто знает/умеет дернуть ногой, вывести/принять данные по шине, и, при этом, ни как не "общается" ни с высокоуровневым классом, ни в низкоур-вом ничего не меняет, то зачем наследовать низ.ур в выс.ур? static и все! :) Я же, в желании "прочно" перейти к С++ программированию, считал использование static C-конструкцией, "недостойной" С++ программирования. :05:

А вот если при вызове функций происходит взаимодействие с элементами классов, ну, например, несколько программных модулей выводят данные по одной и той же шине, и необходимо знать сколько байт данных передано кажным модулем, то без наследования тут никуда. Для каждого модуля должна быть своя копия счетчика, инкрементируемая вызываемой функцией... Ну и т.д. (Конечно этот пример можно "решить" и с использованием static, например добавляя к входным параметрам функции номер модуля... Но эффективность, понятность кода будет "никакой".)

....

В общем, все как обычно - нужно делом, не важно каким, или заниматься всерьез, или не заниматься вообще. Нельзя изучить синтаксис С++ и заявить: "А я теперь умею программировать на С++!"

....

Кстати, использование static вместо наследования сокращает программный код и время выполнения инициализации членов - отсутствуют "пустые" конструкторы, необходимые при наследовании... (и не только из-за этого :) )

 

А если вернуться к вопросу выбора метода ("оптимального" - как я все время "просил" :) ) доступа к функциям класса, то в каждом конкретном случае он свой! В одном случае "хватает" static, а в другом необходима перегружаемая virtual функция.. :) Универсального решения не бывает!

 

Еще раз спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...