![]() |
|
Модераторы: Daevaorn |
![]()
|
|
| Diaus |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 12 Регистрация: 24.7.2004 Репутация: нет Всего: нет |
Давеча создавал свои классы и наткнулся на следующую проблему:
по объективным причинам функция func2 должна быть скрыта от пользователей класса, но доступна для его потомков, при этом func2 должна вести себя по-разному в различных потомках. Но почему-то при реализации выскакивает ошибка, объясните, почему??? пример кода: class A { public: virtual void func1(A& obj)=0; protected: virtual void func2()=0; }; class B:public A { public: virtual void func1(A& obj) { //error C2248: 'func2' : cannot access protected member declared in class 'A' obj.func2(); } protected: virtual void func2() { } }; |
|||
|
||||
| Diaus |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 12 Регистрация: 24.7.2004 Репутация: нет Всего: нет |
В смысле почему я имею доступ к protected функциям объекта своего класса и не имею, если объект родительского класса?
Разве это логично? Если по-вашему логично, то приведите конкретный пример, который бы илюстрировал такую логику. Это компайлится нормально: class A { public: virtual void func1(A& obj) { obj.func2(); } protected: virtual void func2() = 0; }; |
|||
|
||||
| chipset |
|
|||
|
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Экс. модератор Сообщений: 4071 Регистрация: 11.1.2003 Где: Seattle, US Репутация: 27 Всего: 165 |
Примеров много.. Возьми те же унаследованные CMyWnd : public CWnd.
Им нефик делать в private мемберах. Юзай friend, вроде должно помочь.. Проверить не могу - система пять минут назад проинсталенная(с линухом вместе Если не так поправьте.. --------------------
|
|||
|
||||
| Diaus |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 12 Регистрация: 24.7.2004 Репутация: нет Всего: нет |
Ок, с этим ещё можно согласиться(только не private а в protected мемберах).
friends - это в принципе выход(библиотека моя), но всё равно не очень хочется менять базовый класс каждый раз при добавлении потомка. Может кто придумает более красивое решение, потому как менять базовый класс при разработке потомков - это очень плохой стиль? Тут вот ещё одно свинство. Следующий код выдаёт ошибку unresolved external symbol "public: virtual void __thiscall A::Clear(void)" (?Clear@A@@UAEXXZ) причём непонятно почему Нежели в Сях так криво продуманы деструкторы, или для этого случая есть какое-нибудь красивое решение(или обходной путь)? class A { public: virtual void Clear()=0; ~A() { Clear(); } }; class B:public A { public: void Clear() { } }; void main(int argc, char* argv[]) { B b; } Буду премного благодарен. |
|||
|
||||
| Borisff2003 |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 198 Регистрация: 26.2.2004 Где: г. Уфа Репутация: 1 Всего: 1 |
А так не проще. при вызове func1 скласс и так знает кто он такой, зачем ссылку то передовать --------------------
Лень, двигатель прогресса |
|||
|
||||
| Diaus |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 12 Регистрация: 24.7.2004 Репутация: нет Всего: нет |
Дело в том, что нужно вызвать защищённый метод другого, объекта, тоесть чтобы объекты, производные от базового могли обмениваться конфеденциальной информацией.
А насчёт деструкторов, я наверное сам разберусь(книжечку почитаю ещё раз) |
|||
|
||||
| Borisff2003 |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 198 Регистрация: 26.2.2004 Где: г. Уфа Репутация: 1 Всего: 1 |
Только не бить ногами
Такое вот работает, но ничего хорошего в этом нет, так как идет понижающее приведение типов. Надо самому следить за тем чтоб в funcc1 использовать только поля определенные в A. С функциями такое не пройдет!!!
--------------------
Лень, двигатель прогресса |
|||
|
||||
| John_Doe |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 9 Регистрация: 16.7.2004 Репутация: нет Всего: нет |
1) Имхо, у вас ошибка дизайна классов
class A { public: virtual void func1(A& obj)=0; protected: virtual void func2()=0; }; class B:public A { public: virtual void func1(A& obj){obj.func2();} } Если нужно, чтобы потомки обменивались "конфиденциальными" данными, то можно просто определить protected структуру, описывающую эти данные, а сам метод вынести в public. Также можно извернуться с pimpl (pointer to implementation), чтобы скрыть внутренности. Можно подумать о паттернах. Можно ещё что-нить придумать 2) Вообще-то, деструкторы могут быть виртуальными, т.е. данный код: class A { public: virtual void Clear()=0; ~A() {Clear();} }; Правильнее писать так: class A { public: virtual ~A()=0; }; И не удивляться кривизне С++ |
|||
|
||||
| achmed |
|
|||
|
Бывалый ![]() Профиль Группа: Участник Сообщений: 150 Регистрация: 12.4.2004 Репутация: нет Всего: нет |
класс A это интерфейс, вызвать методы нужно по интерфейсному указателю
|
|||
|
||||
| Hroft |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 310 Регистрация: 20.10.2003 Где: Москва Репутация: нет Всего: 3 |
Дак ведь к членам тоже нельзя защищенным через параметр-указатель пробраться. Так что метод, определенный в потомке, не сможет обратиться к этой структуре. А еще нельзя вызывать из конструктора чисто виртуальные функции вообще, а просто виртуальные не рекомендуется (хотя по-моему тоже просто нельзя). Причем g++ это отлавливает при компиляции, VC++6.0 при линковке (непонятно как), а Билдер только в рантайме (мол, Pure virtual function called). Но тут понятно - конструктор пытается вызвать функцию с отсутствующим телом, ни о каких виртуальных таблицах он ничего не знает. А вот с деструктором по-идее не так - он вполне может посмотреть по v-таблице, но... error: abstract virtual `virtual void A::clear()' called from destructor, говорит g++. То же, что и с конструктором. Добавлено @ 14:45 А с указателем то же самое, achmed. Но вообще да, ссылками как-то нехорошо, для них вообще виртуальность будет работать? Я не помню, проверить лень. |
|||
|
||||
| C/L |
|
|||
|
Unregistered |
Дело в том, что при создании/удалении потомка вызывается сначала конструктор/деструктор родителя, при чем с VTable родителя. А просто виртуальные функции вызывать можно - всё таки какая-то реализация всё равно будет.
|
|||
|
||||
| Diaus |
|
|||
|
Новичок Профиль Группа: Участник Сообщений: 12 Регистрация: 24.7.2004 Репутация: нет Всего: нет |
И всё-таки вопрос, как организовать обмен конфеденциальной информацией между
классами, производными от некоторого базового класса, остаётся открытым. Напомню - это главный вопрос в этой теме. |
|||
|
||||
| Guest |
|
|||
|
Unregistered |
Поправляю своё предыдущее сообщение.
Конструкторы и деструкторы классов не наследуются. При создании объекта сначала вызывается конструктор родителя а потом потомка. При удалении наоборот сначала вызывается деструктор потомка потом родителя. Становится понятно почему используется VTable родителя: при создании объекта VTable потомка еще не существует, а при удалении VTable потомка уже оказывается удален. По поводу доступа protected. Я считаю логичным, что невозможен доступ к защищенным членам объекта другого класса, пусть и произошедшим от одного родителя. Protected создан только для доступа к своим наследуемым членам. То, что нет вида доступа для общения между классами-потомками конечно большое упущение(опущение) Мелкософта. Надо пожаловаться - может в 8 версии VC++ будет. То, что член защищен не значит, что его нельзя вскрыть. На это способен только извращенский оператор reinterpret_cast, правда ссылку на объект придется заменить указателем. Фактически это предложил Borisff2003, только для данных. А это действует и для виртуальных функций. Да так даже и приват можно вскрыть, я проверял.
Как и предупреждал Borisff2003, при использовании reinterpret_cast надо следить, чтобы небыло обращений к членам потомка - компилятор это пропустит, а что сделает получившаяся программа неизвестно. |
|||
|
||||
| Олег М |
|
||||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 436 Регистрация: 10.6.2004 Где: Москва Репутация: 7 Всего: 7 |
Всё правильно. Так работать не должно да и не будет. По стандарту. Нужно, чтобы класс В был другом класса А class A { public: virtual void func1(A& obj)=0; protected: virtual void func2()=0; friend class B; }; class B:public A { public: virtual void func1(A& obj) {obj.func2();} protected: virtual void func2(){} }; Тогда всё будет нормально
Почему? Всё отработает корректно. Мы же только снимаем модификатор "протектед" |
||||
|
|||||
| Borisff2003 |
|
||||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 198 Регистрация: 26.2.2004 Где: г. Уфа Репутация: 1 Всего: 1 |
Тут шел разговор про такой случай
Это сообщение отредактировал(а) Borisff2003 - 2.8.2004, 11:14 --------------------
Лень, двигатель прогресса |
||||
|
|||||
| Олег М |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 436 Регистрация: 10.6.2004 Где: Москва Репутация: 7 Всего: 7 |
Borisff2003
Ясно. Как всегда был невнимателен. Это тоже будет работать корректно, но только если в функ1 будут передаваться указатели на экземпляры класса В или его потомков. В остальных случаях работать нихрена не будет. А зачем вам вообще все эти извращения? Или так, полёт фантазии? А что такое "Бздям"? |
|||
|
||||
| C/L |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 107 Регистрация: 31.7.2004 Где: Самара Репутация: 1 Всего: 1 |
Олег М , хороший выход с классами друзьями.
Но что, если заранее неизвестно сколько и каких будет классов потомков, а исходника реализации класса (*.cpp) нету. Добавлено @ 20:37 Это я неподумавши написал. Надо же изменить только определение класса, а не реализацию. Но все равно лучше бы был специальный тип доступа, чем каждый раз лезть в *.h файл разработчика класса (если класс не твой). |
|||
|
||||
| C/L |
|
||||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 107 Регистрация: 31.7.2004 Где: Самара Репутация: 1 Всего: 1 |
Небольшая поправка, Олег М. С экземплярами класса A (если он не абстрактный) и его потомками тоже будет работать (при условии обращений к членам класса A), я уже проверял. Но предложенный тобою способ конечно лучше.
Какие могут быть остальные случаи, если передается именно указатель на объект класса A. Все потомки будут приводится к нему, а другие классы просто не пройдут. Заранее извиняюсь если я чего-то не понял. Это сообщение отредактировал(а) C/L - 3.8.2004, 01:55 |
||||
|
|||||
| C/L |
|
|||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 107 Регистрация: 31.7.2004 Где: Самара Репутация: 1 Всего: 1 |
Хотя про friends еще в самом начале писал chipset. Только он не уточнил как именно их использовать.
Это сообщение отредактировал(а) C/L - 3.8.2004, 01:59 |
|||
|
||||
| Олег М |
|
||||||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 436 Регистрация: 10.6.2004 Где: Москва Репутация: 7 Всего: 7 |
Это ни вкоем случае делать нельзя. Если нужно вызывать протектед-члены класса - наследуйся от него и вызывай сколько угодно. Для того и сделано.
Это естественно. Всё говорилось для данного конкретного случая.
Ну да. Хотя действительно, выразился он несколько туманно: "Юзай friend, вроде должно помочь.." |
||||||
|
|||||||
| Borisff2003 |
|
|||
![]() Бывалый ![]() Профиль Группа: Участник Сообщений: 198 Регистрация: 26.2.2004 Где: г. Уфа Репутация: 1 Всего: 1 |
C/L
Но там могут быть потомки классса A в которых нет func3. --------------------
Лень, двигатель прогресса |
|||
|
||||
| C/L |
|
||||||
|
Шустрый ![]() Профиль Группа: Участник Сообщений: 107 Регистрация: 31.7.2004 Где: Самара Репутация: 1 Всего: 1 |
Предлагаете что-то типа этого?
Здорово!!! И овцы сыты и волки целы!!! |
||||||
|
|||||||
| Олег М |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 436 Регистрация: 10.6.2004 Где: Москва Репутация: 7 Всего: 7 |
C/L
Хитро. Я предлагал не совсем это. Но работает. При условии, что класс А не будет абстрактным, в частности func1. А то в классе С func1 это уже совсем другая функция. |
|||
|
||||
![]()
|
| Правила форума "С++:Общие вопросы" | |
|
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |