Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Доступ к методам (функциям) класса.
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > Программирование
Stas633
Допустим:
- имеются два класса TOne и TTwo;
- в TOne определена ф-ция FuncOne();
- классы определены в разных единицах компиляции (файлах);
- созданы объекты (глобальные) этих классов (mOne и mTwo) в третьем файле - "main.cpp".
Вопрос:
Как из класса TTwo получить доступ (использовать) функцию FuncOne() класса TOne?

1. наследовать TOne в TTwo
2. перед описанием класса TTwo объявить объект классаа TOne: extern TOne mOne.
3. ...?
...
А если таких "вложений" больше? (Т.е. функции TTwo, использующие TOne, необходимы в TThree)

Что лучше? Как правильнее? Есть ли разница в испозовании стека?

Прим. Например: TOne - класс описывающий работу с индикатором (LCD), а TTwo - класс формирования данных для вывода на индикатор. Рассматривается ситуация одного процесса (не RTOS).

Прим.1 Вопрос задаю здесь, так как обсуждение темы о выделении раздела форума для "чистого" программирования, увы, ни к чему не привело. smile.gif Господа Админы/Модераторы извините, просмотрел..
igorenja
Цитата(Stas633 @ Oct 14 2008, 03:44) *

Я думаю оптимален первый вариант.
dxp
Цитата(Stas633 @ Oct 14 2008, 04:44) *
Что лучше? Как правильнее? Есть ли разница в испозовании стека?

Прим. Например: TOne - класс описывающий работу с индикатором (LCD), а TTwo - класс формирования данных для вывода на индикатор. Рассматривается ситуация одного процесса (не RTOS).

Что лучше и правильнее, зависит от задачи. Если один класс является другим + некоторые свои расширения, то имеет смысл наследовать, если один класс содержит функциональность другого, то имеет смысл включить один в другой. Если классы живут сами по себе, то ничего вышесказанного делать не нужно, а общение меж ними организовать на открытой части (public). Для того, чтобы сделать объект глобальным (не важно, какой объект - класса или встроенного типа), нужно объявить объект в одном из исходных файлов, а в одном из заголовочных, который включается во все исходные, объявить этот же объект с квалификатором extern. Тогда объект будет виден и доступен везде, где есть это объявление.
Stas633
bb-offtopic.gif
Ув. dxp, спасибо за Ваши советы/рекомендации (в другой ветке)!!!
... При внешней схожести С и С++ очень разные языки... ...разные тем, что на них думать надо совсем по-разному... ...
Попробуйте думать над программой на уровне объектов реального мира, ..., тогда реализация ляжет на классы почти сама собой... (с) dxp

Начал ознакомление с работами Страуструпа и Буча ("настольной книгой", до последнего времени, был "Самоучитель..." Шилдта) и сразу понял, что, используя синтаксис и правила С++, пишу на Си! Отсюда и "проблемы" вида: ".. как сделать функцию доступной.." и т.д.
При написании программы я использовал "прямое" наследование для получения доступа к функциям базовых классов. Т.е. при создании объекта производного класса создавался объект/копия базового класса и таких копий было "множество", хотя достаточно одной единственной. Последствия, в виде "нехватки" ОЗУ, очевидны... Отсюда и мой интерес к получению "беззатратного" доступа к "чужим" функциям.
Программу нужно писать на С++, а не на Си, изпользуя С++ синтатсис и правила!! smile.gif Т.е. начинать нужно с рисования структуры связей и т.д.
Спасибо еще раз! a14.gif

Цитата(dxp @ Oct 14 2008, 12:12) *
... Если классы живут сами по себе, то ничего вышесказанного делать не нужно, а общение меж ними организовать на открытой части (public)....

Что Вы имеете ввиду? Если не через наследование, то как тогда?
dxp
Цитата(Stas633 @ Oct 15 2008, 03:58) *
Что Вы имеете ввиду? Если не через наследование, то как тогда?

Не очень понимаю затруднение. Вот имеем код:

Код
class TSlon
{
public:
    TSlon();
    void f();
};

class TMamont
{
public:
    TMamont();
    void f();
};
...

TSlon slon;
TMamont mamont;

void TMamont::f()
{
    slon.f();
}


как видно, спокойно вызываем из функции одного класса функцию другого. Все public члены классов доступны внешнему окружению без каких-либо ограничений. Это так задумано, и это логично. Управление доступом - основа инкапсуляции/абстракции. Представление прячем в private секцию объекта, а общение (функциональность) делаем через интерфейс - public секцию. Как правило, в public секции присутствуют только функции. Т.е. интерфейс - это действия, которые может выполнять объект, а доступ к данным снаружи как правило не нужен и чреват хаками и ошибками. Хотя, конечно, язык не запрещает делать открытыми и данные-члены.

В некоторых случаях возникает необходимость дать доступ к закрытой части одного класса другому классу, но при это не хочется делать эту часть открытой, чтобы к ней не было доступа всем. В этом случае можно объявить класс, который нуждается в доступе, как friend (друг) в классе, к представлению которого требуется доступ.

Или вам что-то другое надо?
alexander55
Цитата(Stas633 @ Oct 14 2008, 01:44) *


Тут надо смотреть, как правильнее по смыслу.
Можно класс унаследовать.
class TTwo: public TOne {
...
};
Можно сделать экземпляр класса TOne членом класса TTwo
class TTwo {
...
TOne One;
...
};
Работать будет.
Хорошо об этих проблемах написано в книге ""Философия Java".
Stas633
Цитата(dxp @ Oct 15 2008, 08:32) *
Или вам что-то другое надо?

Нет, "другого" не нужно. Вопрос в разной терминологии. Вы это называете "...общение ... на открытой части...", а я называл "..объявлением объекта перед использованием класса". Ваша терминология конечно правиль-на (-нee). Вообще, "одинаковый алфавит" - это начало начал при обсуждении любого вопроса.
В предложенном варианте необходимо "помнить" (держать в памяти) где и с каким именем объявлен объект slon. Если он объявлен в main - файле, то в в файле описания TMamont нужно этот обект объявлять как extern и т.д. Т.е. опять "возврат" к Си.
bb-offtopic.gif
По "точный" ответ на "точный" вопрос вспомнился старый анекдот... Когда воздухоплаватель на воздушном шаре приземлился в незнакомой местности и спросил у проходившего мимо программиста: "Где я нахожусь?". И тот, после недолгого раздумья, ответил: "Вы находитесь в корзине воздушного шара." Ответ правильный и точный но... smile.gif
(не примите, плз., это на свой счет, это так, к слову).
...
Правильнее, на мой "просветленный" smile.gif взгляд, построить "объектную модель программы" и создать иерархию классов с использованием абстактных классов (гл.12, "Язык программирования C++", Б.Страуструп). Хотя для Си - программиста это и покажется более сложным. Кстати, предложенную Вами реализацию меню через наследование абстактного класса успешно реализовал, правда, как оказалось, не понимая сути smile.gif


Цитата(alexander55 @ Oct 15 2008, 09:19) *
Тут надо смотреть, как правильнее по смыслу.
...
Хорошо об этих проблемах написано в книге ""Философия Java".

Вы правы - "Философия...". smile.gif Т.е. нужно менять подход, а не искать "средства".
В общем-то, вопрос о способах доступа раскрыт, как мне кажется, полностью.
Главный вывод это то, что нельзя (неправильно, трудно, неэффективно...) использовать объекты для описания процедур.
Нужно или "заниматься" (программировать) обектно ориентированным программированием, или процедурным. Написана программа на Си - нет смысла пытаться "подогнать" ее под С++. А вот использовать готовые куски кода (функции) - пожалуйста.
Спасибо!

P.S. Способов два:
- через наследование. НО! Если базовые классы не являются абстрактными, но наследование приводит к избыточному наличию копий базовых классов (обектов) (если не прав - поправьте);
- через указатель на класс (объект). НО! Здесь проявляется вся "прелесть" продердурного программирования и смысл перехода от Си к С++ теряется.
... предложенное dxp "включить один (класс) в другой" не рассматриваю, так как производных классов несколько, и включение базового в каждый .............

Вместе с тем, любой из вариантов работоспособен и не является ошибочным. И, в зависимости от ситуации, может быть применен.
IgorKossak
Цитата(Stas633 @ Oct 14 2008, 00:44) *
Вопрос:
Как из класса TTwo получить доступ (использовать) функцию FuncOne() класса TOne?

3. ...?

Никто почему-то не сказал о средстве friend.
Т. е. в классе TTwo обьявить другом класс TOne.
vik0
Вставлю и я свои 5 копеек smile.gif
1. Если у Вас TOne - таки LCD, то можно использовать статические функции. Ведь LCD у вас в системе один, правильно?
Код
class TOne
{
  static int memberVar;
public:
  static void Foo() {}
};
.....
class TTwo
{
public:
  void Bar()
  {
     TOne::Foo();
  }
};

2. Если TOne - не LCD (или у вас в системе их несколько smile.gif ), тогда правильнее передавать указатель на TOne в ф-цию ининциализации
Код
class TOne
{
public:
  void Foo() {}
};
.....
class TTwo
{
  TOne* oneInstance;
public:
  void Init(TOne* one)
  {
    oneInstance = one;
  }

  void Bar()
  {
     oneInstance->Foo();
  }
};
...
TOne mOne;
TTwo mTwo;

int main(int, char**)
{
  mTwo.Init(&mOne);
  mTwo.Bar();
  ....
}


А friend тут не причем. У человека, по-моему, проблемы не с правами доступа к членам класса, а с доступом (работой) к глобальным переменным.
Stas633
Чтобы не было "гаданий на кофейной гуще" опишу коротко "историю" возникновения вопроса... Хоть с темой это и не связано.
- была рабочая программа на Си. Программа содержала готовые "блоки" для работы с LCD, интерфейсы I2C, SPI, UART, работа с кнопками, энкодером и т.д;
- при "проектировании" нового изделия было принято решение о переходе на С++ (причины принятия решения опускаю);
- мне показалось достаточным "обозвать" (объявить) блоки классами. Для доступности функций интерфейса (LCD и т.д.) и проводилось прямое, читай - бездумное, наследование.
- пока объема озу (стека) хватало для размещения всех копий базовых классов все было "спокойно"... А как только порог был превышен, так сразу и появился, вопрос, вынесенный в топик. Поэтому подтекст вопроса примерно такой: "Как можно обратиться к функции, объявленной в другом классе (члену другото класса - так правильнее smile.gif ), без "потери" памяти на это обращение". Но в таком виде писать вопрос в TOP стыдно .. (видимо напрасно стыжусь - учиться никогда не поздно).

И friend и static весьма "полезны": friend позволяет получить доступ без создания копий (т.е. буквально то, что я хотел), а обявление элемента с квалификатором static гарантирует, что будет создана только одна копия этого элемента. (Пишу очевидные вещи для таких как я начинающих С++).

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

"...Чем лучше программист знает С, тем труднее будет для него при программировании на С++ отойти от
стиля программирования на С...." (с) Бьерн Страуструп.

....
А чем наследование лучше/хуже доступа через friend? (для рассматриваемого случая)
dxp
Цитата(Stas633 @ Oct 15 2008, 15:09) *
В предложенном варианте необходимо "помнить" (держать в памяти) где и с каким именем объявлен объект slon. Если он объявлен в main - файле, то в в файле описания TMamont нужно этот обект объявлять как extern и т.д. Т.е. опять "возврат" к Си.

Никакого возврата нет, т.к. С++ - это надмножество С, и использует те же средства в полном объеме. Помнить, под каким именем объявлен объект slon не надо - он объявлен под именем slon. smile.gif Для организации доступа к глобальному объекту нужно (в данном случае):
  1. Определения классов поместить в заголовочный файл, который включается во все исходные файлы проекта.
  2. В одном из исходных файлов объявить сами объекты.
  3. В вышеуказанном заголовочном файле поместить следующие объявления:
    extern TSlon slon;
    extern TMamon mamont;
Все, теперь оба объекта будут доступны везде. Если надо доступ к потрохам, об этом уже сказано выше.

Цитата(IgorKossak @ Oct 15 2008, 15:38) *
Никто почему-то не сказал о средстве friend.
Т. е. в классе TTwo обьявить другом класс TOne.

Почему не сказал? Было предложено. smile.gif

Цитата(Stas633 @ Oct 15 2008, 18:18) *
А чем наследование лучше/хуже доступа через friend? (для рассматриваемого случая)

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

Далее. Допустим есть объекты "слон", "мамонт" и "стадо". Поскольку стадо состоит из животных, то логично, чтобы объект "стадо" включал в себя объекты слонов и мамонтов (возможно, в виде массивов). Это отношение включения.

Ну, и наконец, пусть есть объекты "слон" и "бегемот". Тут ни один из другого не получается и ни один другого включать не может (если только не сожрет smile.gif ). Но взаимоотношения между ними строить можно (например, один прогнал другого с пастбища или еще что-то, я не знаток зоологии smile.gif ). В этом случае это просто отдельные независимые объекты, которые живут сами по себе, но могут пользоваться открытыми функциями-членами друг друга, как я приводил выше. Можно даже закрытыми, если один объявить другом другого (хотя с трудом представляю себе такую дружбу в натуре smile.gif ). Однозначно имеет смысл тут завести абстрактный базовый класс Animal (с виртуальной функций eat smile.gif ), и от него наследовать обоих зверушек. Это уже позволит их обоих накормить единоорабазно в цикле. smile.gif

Словом, вам надо определиться, какие у вас отношния между объектами, и строить код уже исходя из этого. А на ощупь и методом тыка ничего хорошего не получится.
Stas633
Цитата(dxp @ Oct 15 2008, 15:59) *
Словом, вам надо определиться, какие у вас отношния между объектами, и строить код уже исходя из этого. А на ощупь и методом тыка ничего хорошего не получится.

smile.gif
Спасибо. Ассоциативное объяснение самое доходчивое, а главное "заставляет" мыслить "объектами"... хоть "слонами", хоть "яблоками". smile.gif
"Обложился" книгами.. Учу "зоологию". smile.gif
IgorKossak
Цитата(dxp @ Oct 15 2008, 14:59) *
Почему не сказал? Было предложено. smile.gif

Виноват-с, проглядел.
Очень уж напрашивалось выделить отдельным пунктом.
?ELF
Цитата(dxp @ Oct 15 2008, 17:59) *
...
...
...

Словом, вам надо определиться, какие у вас отношния между объектами, и строить код уже исходя из этого. А на ощупь и методом тыка ничего хорошего не получится.

Спасибо! a14.gif
Хорошая аллегория.
Прочитал Ваш ответ -- словно "в юности побывал" -- "Страуструпп Б. - Язык программирования С++., Диасофт, Киев, 1993."

---
Вообще, такие вещи, на мой взгляд, более логично и более глубоко описывались в Prolog-е.
Создал "базу знаний" -- и вперёд!
Используй термы, предложения, правила, предикаты...
Почти умер язык, к сожалению.
Не выдержал коммерческого натиска C++.
bookevg
Цитата(Stas633 @ Oct 15 2008, 14:18) *
Чтобы не было "гаданий на кофейной гуще" опишу коротко "историю" возникновения вопроса... Хоть с темой это и не связано.
- была рабочая программа на Си. Программа содержала готовые "блоки" для работы с LCD, интерфейсы I2C, SPI, UART, работа с кнопками, энкодером и т.д;
...
И friend и static весьма "полезны": friend позволяет получить доступ без создания копий (т.е. буквально то, что я хотел), а обявление элемента с квалификатором static гарантирует, что будет создана только одна копия этого элемента. (Пишу очевидные вещи для таких как я начинающих С++).


В таких случаях как приведен выше использую класс со всеми методами объявленными как static (в C# почти что все библиотечные функции статические методы класса). С friend-м надо лучше концепцию продумать.


Цитата(Stas633 @ Oct 15 2008, 14:18) *
....
А чем наследование лучше/хуже доступа через friend? (для рассматриваемого случая)

Наследование хорошо использовать но, надо заметить, что возможно два основных случая:
-замещение;
-виртуальность.
Использование виртуальности ведет к повышенному расходу памяти (создание v-таблицы) и снижение производительности
Stas633
Цитата(bookevg @ Nov 6 2008, 15:45) *
...

Спасибо. Согласен... Наследование для решения вопроса доступа - это, как минимум, не экономно.
...
Если говорить по теме, то вопрос доступа решил так, как рекомендовал 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. Структура сложных систем" и вопросов о полезности и естественности перехода к ООП не остается. Мир вокруг нас состоит из Объектов, а не из Процедур. И живем мы, используя Объекты и их какие-то свойсва (Процедуры), а не Процедуры каких-то Объектов.
Stas633
Поиск "оптимального" решения продолжается....
Позволю себе напомнить "проблему"...
Есть некий объект (класс), допустим, модуль отладки (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. Это очень "смахивает" на Си-подход, но лучшего в голову не приходит... Хотя спинной мозг "подсказывает", что не все так однозначно... smile.gif

Просвятите, плз.
bookevg
Цитата(Stas633 @ Nov 21 2008, 23:03) *
Поиск "оптимального" решения продолжается....
Позволю себе напомнить "проблему"...
Есть некий объект (класс), допустим, модуль отладки (DBGU) через UART. Естественно, объект может (и должен) инициализироваться, принимать и передавать инфу, и др.
Свойства (функции, методы....) этого объекта (класса), ес-но должны быть доступны другим, если не сказать всем остальным, объектам. При этом, все остальные обекты могут находиться в "родительско-наследственных" отношениях. Как получить доступ к функциям DBGU?

Я так понимаю различные классы будут обращаться к DBGU - т.о. надо сделать систему доступа к общему ресурсу или создать список заданий для DBGU
Цитата(Stas633 @ Nov 21 2008, 23:03) *
То, что использовать наследование DBGU для получения доступа к его свойсвам, не рационально описано выше в теме. Содание класса TDBGU как виртуального гарантирует, что при косвенном ("вложенном") наследовании "верхних" классов не будет создано двух копий TDBGU. А если virtual TDBGU наследуется двумя "отдельными" (разными, самостоятельными...) классами, то для каждого будет создана своя копия TDBGU... Или я не прав? И что вообще означает "производный класс содержит копию базового класса"? Про "создание" копий переменных базового класса при наследовании понятно, а как наследуются функции? Просмотр "откомпилированного" кода и здравый смысл подсказывает, что функции наследуются "беззатратно". Но если это так, то при условии остутсвия переменных в базовом классе его копия ничего не "весит"?

Мне кажется, что вы неправильно трактуете суть полиморфизма - советую прочитать Дж.Либерти - Освой С++ за 21 день. Попробую автора процитировать:
Можно объвить множество окон разных типов, включая диалоговые, прокручиваемые и поля списков и т.д.), после чего создавать их в программе с помощью единственного виртуального метода draw(). Создав указатель на базовое окно и присваивая этому указателю адреса объектов производных классов, можно обращаться к методу draw() независимо от того, с каким из объектов в данный момент связан указатель. Причем всегда будет вызываться вариант метода, специфичный для класса выбранного объекта.
Цитата(Stas633 @ Nov 21 2008, 23:03) *
Объявление глобального объекта mDBGU с использованием там "где нужно" конструкции extern ... mDBGU так же не лишено недостатков (как способ). Во-первых, глобальный объект "размещается" в "глобальной" области, при этом размещается "раз и навегда".

А как по вашему работают cin и cout - ведь вы ведь нигде от них не наследуетесь, а они отвечают за ввод и вывод информации
Цитата(Stas633 @ Nov 21 2008, 23:03) *
Ну и, во-вторых, нарушается один из первейших принципов ООП - инкапсуляция. (Второе, конечно, относится скорее к стилю программирования нежели к "ошибкам", но если не выполнять правил, то зачем переходить на CPP?).

Ничего не нарушается.
Цитата(Stas633 @ Nov 21 2008, 23:03) *
Кстати, натолкнуло на эту мысль упорное "нежелание" IAR инициализировать глобальные объекты при startup'e.

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

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

Вообще никогда friend-ом не пользуюсь
Цитата(Stas633 @ Nov 21 2008, 23:03) *
Остается доступ к свойствам (функциям) DBGU через их объявление с квалиф. static. Ранее я писал, что "...static гарантирует, что будет создана только одна копия этого элемента...". Это справедливо, но только для переменных (может я ошибаюсь?). Для функций же это квалификатор "устанавливает" глобальную "видимость" и "доступность" даже до инициализации объекта mDBGU..и только.
Таким образом, объявив ф-цию как в staticах, можно ее "использовать" где угодно, конечно предварительно "показав" ее (класс) через #include. Это очень "смахивает" на Си-подход, но лучшего в голову не приходит... Хотя спинной мозг "подсказывает", что не все так однозначно... smile.gif

Static переменные и методы нужны для действий, которые оперируют общими ресурсами для всех экземпляров класса.
Также можно создавать только static классы - что я очень часто и использую - на производительность это никак не сказывается, т.к. компилятор все это сводит к С-реализации
Stas633
Цитата(bookevg @ Nov 22 2008, 11:28) *
Я так понимаю различные классы будут обращаться к DBGU - т.о. надо сделать систему доступа к общему ресурсу или создать список заданий для DBGU

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

Цитата(bookevg @ Nov 22 2008, 11:28) *
Мне кажется, что вы неправильно трактуете суть полиморфизма - советую прочитать Дж.Либерти - Освой С++ за 21 день. Попробую автора процитировать:
Можно объвить множество окон разных типов, включая диалоговые, прокручиваемые и поля списков и т.д.), после чего создавать их в программе с помощью единственного виртуального метода draw(). Создав указатель на базовое окно и присваивая этому указателю адреса объектов производных классов, можно обращаться к методу draw() независимо от того, с каким из объектов в данный момент связан указатель. Причем всегда будет вызываться вариант метода, специфичный для класса выбранного объекта.

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

Цитата(bookevg @ Nov 22 2008, 11:28) *
А как по вашему работают cin и cout - ведь вы ведь нигде от них не наследуетесь, а они отвечают за ввод и вывод информации

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

Цитата(bookevg @ Nov 22 2008, 11:28) *
Ничего не нарушается.

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

Цитата(bookevg @ Nov 22 2008, 11:28) *
До входа в main() все глобальные классы проинициализированы, т.е. вызваны все конструкторы.

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

Цитата(bookevg @ Nov 22 2008, 11:28) *
Вообще никогда friend-ом не пользуюсь

А почему?

Цитата(bookevg @ Nov 22 2008, 11:28) *
Static переменные и методы нужны для действий, которые оперируют общими ресурсами для всех экземпляров класса.

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

Цитата(bookevg @ Nov 22 2008, 11:28) *
Также можно создавать только static классы - что я очень часто и использую - на производительность это никак не сказывается, т.к. компилятор все это сводит к С-реализации

Другими словами, если не смог (к Вам никакого отношения не имеет!) написать на С++ используй static и пиши как на С... 05.gif
...
И все-таки, если класс не содержит ресурсов (переменных, данных), то будет ли формироваться избыточный код при его "многократном" наследовании?
bookevg
Цитата(Stas633 @ Nov 22 2008, 13:09) *
Совершенно верно. Только ЧТО делать понятно, а вот что на счет того КАК делать?
Не соглашусь с Вами. В приведенной цитате речь идет о методах, т.е. виртуальных функциях . Которые естественно должны иметь свое "наполнение" в производных классах. Но я рассуждал не о наследовании виртуальных функций и полиформизме, а о наследовании virtual класса класса как virtual.

Зачем нужно, чтобы весь класс был виртуальным - надо только, чтобы методы были и этого достаточно
Цитата(Stas633 @ Nov 22 2008, 13:09) *
Как я понимаю, виртуальный класс вовсе не "обязан" содержать виртуальные функции.

А зачем тогда он нужен - виртуальность нужна как раз только для методов
Цитата(Stas633 @ Nov 22 2008, 13:09) *
Более того, виртульные функции можно создавать только в абстактнх классах. То есть объектов таких классов создано быть не может. Просто "не получится". Не так ли?

Еще как можно использовать - т.е. объявил класс СTimers1 как обычно, создал вирт.метод СTimers1::Update, затем породил класс СTimers2 от СTimers1 и переписал СTimers1::Update и создал два экземляра СTimers1 Timers1; и СTimers2 Timers2 и работаешь с ними.
Цитата(Stas633 @ Nov 22 2008, 13:09) *
Извините, не понял к чему это... Я имел ввиду нецелесообразность обявления global объектов, если есть возможность сделать их локальными.

Я как понял вашу задачу: вы хотите, чтобы другие объекты через экземпляр класса DBGU общались с внешним миром - но ведь на PC-машинах все наши объекты общаются с внешним миром или через cin/cout или GUI
Цитата(Stas633 @ Nov 22 2008, 13:09) *
Спорно. Если объект объявляется/выносится за пределы своего "ареала обитания", то разве это соответсвует принципу encapsulation? Зачем в список фруктовых деревьев сада вносить функцию измерения размера косточки плода? Ведь к этой функции все равно придется обращаться, как минимум, через Плод. (На точности примера не настаиваю)

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

Незамечал, я обычно всегда делаю свой Cstartup.s79, так что мог и ошибиться, но вроде еще с AVR глобаль.объекты инициализировал компилятор - даже пришлось убрать инициализацию из конструкторов, чтоб все происходило по порядку определяемому мною
Цитата(Stas633 @ Nov 22 2008, 13:09) *
А почему?

Пока нужды не возникало использование friend, если и порывался. то находил другие пути выхода из ситуации, да и не все embedded компиляторы поддерживали это лет 4-6 назад.
Цитата(Stas633 @ Nov 22 2008, 13:09) *
Т.е. данными?

Необязательно, м.б. и общие действия, но в принципе все равно все сводится к данным
Цитата(Stas633 @ Nov 22 2008, 13:09) *
Другими словами, если не смог (к Вам никакого отношения не имеет!) написать на С++ используй static и пиши как на С... 05.gif

Все проекты разрабатываются в Microsoft Visual Studio - при оформлении классом одновременно улучшается читабельность кода
Цитата(Stas633 @ Nov 22 2008, 13:09) *
...
И все-таки, если класс не содержит ресурсов (переменных, данных), то будет ли формироваться избыточный код при его "многократном" наследовании?

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

Спасибо Вам за участие. a14.gif
Свой выбор "оптимального" способа я сделал. smile.gif Правда, только благодаря помощи dxp. smile.gif
Не буду коверкать "первоисточник" - завтра вставлю цитатой... Смысл сказанного прост до банальности. smile.gif

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


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

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

Еще раз спасибо.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.