Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Неужели так и нужно?! Это баг или так задумано? 
:(
    Опции темы
Diaus
  Дата 24.7.2004, 08:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 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()
{
}
};

PM MAIL   Вверх
Diaus
Дата 24.7.2004, 09:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 24.7.2004

Репутация: нет
Всего: нет



В смысле почему я имею доступ к protected функциям объекта своего класса и не имею, если объект родительского класса?
Разве это логично? bored.gif
Если по-вашему логично, то приведите конкретный пример, который бы илюстрировал такую логику. wink.gif

Это компайлится нормально:
class A
{
public:
virtual void func1(A& obj)
{
obj.func2();
}
protected:
virtual void func2() = 0;
};

PM MAIL   Вверх
chipset
Дата 24.7.2004, 09:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 4071
Регистрация: 11.1.2003
Где: Seattle, US

Репутация: 27
Всего: 165



Примеров много.. Возьми те же унаследованные CMyWnd : public CWnd.
Им нефик делать в private мемберах. Юзай friend, вроде должно помочь.. Проверить не могу - система пять минут назад проинсталенная(с линухом вместе rolleyes.gif ) компилер только начну ставить...
Если не так поправьте..


--------------------
Цитата(Jimi Hendrix)
Well, I stand up next to a mountain
And I chop it down with the edge of my hand
PM MAIL WWW   Вверх
Diaus
Дата 24.7.2004, 10:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 24.7.2004

Репутация: нет
Всего: нет



Ок, с этим ещё можно согласиться(только не private а в protected мемберах).
friends - это в принципе выход(библиотека моя), но всё равно не очень хочется менять базовый класс каждый раз при добавлении потомка. hmmm.gif
Может кто придумает более красивое решение, потому как менять базовый класс при разработке потомков - это очень плохой стиль?

Тут вот ещё одно свинство.
Следующий код выдаёт ошибку unresolved external symbol "public: virtual void __thiscall A::Clear(void)" (?Clear@A@@UAEXXZ)
причём непонятно почему
Нежели в Сях так криво продуманы деструкторы, или для этого случая есть какое-нибудь красивое решение(или обходной путь)? thumbs-up.gif

class A
{
public:
virtual void Clear()=0;
~A()
{
Clear();
}
};

class B:public A
{
public:
void Clear()
{
}
};

void main(int argc, char* argv[])
{
B b;
}
Буду премного благодарен.

PM MAIL   Вверх
Borisff2003
Дата 26.7.2004, 05:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 198
Регистрация: 26.2.2004
Где: г. Уфа

Репутация: 1
Всего: 1



Код

class A
{
public:
    virtual void func1()=0;
protected:
    virtual void func2()=0;
};

class B:public A
{
public:
    virtual void func1()
   {
        func2();
    }
protected:
    virtual void func2()
    {
    }
};


А так не проще. при вызове func1 скласс и так знает кто он такой, зачем ссылку то передовать notify.gif


--------------------
Лень, двигатель прогресса
PM MAIL WWW ICQ   Вверх
Diaus
Дата 26.7.2004, 09:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 24.7.2004

Репутация: нет
Всего: нет



Дело в том, что нужно вызвать защищённый метод другого, объекта, тоесть чтобы объекты, производные от базового могли обмениваться конфеденциальной информацией.
А насчёт деструкторов, я наверное сам разберусь(книжечку почитаю ещё раз)
PM MAIL   Вверх
Borisff2003
Дата 26.7.2004, 10:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 198
Регистрация: 26.2.2004
Где: г. Уфа

Репутация: 1
Всего: 1



Только не бить ногами tounge.gif
Такое вот работает, но ничего хорошего в этом нет, так как идет понижающее приведение типов. Надо самому следить за тем чтоб в funcc1 использовать только поля определенные в A. С функциями такое не пройдет!!!

Код


class A
{
public:
A(int ipr):pr(ipr){};
virtual void func1(A* obj)=0;
protected:
virtual void func2()=0;
int pr;
};

class B:public A
{
public:
B():A(5){};
virtual void func1(A* obj)
{
 int i=reinterpret_cast<B*>(obj)->pr;
}
protected:
virtual void func2()
{
}
};

class C:public A
{
public:
C():A(6){};
virtual void func1(A* obj)
{
 int i=reinterpret_cast<C*>(obj)->pr;
}
protected:
virtual void func2()
{
}
};




int main(int argc, char* argv[])
{
B b;
B b2;
C c;
b.func1(&b2);
b.func1(&c);
c.func1(&b);

}



--------------------
Лень, двигатель прогресса
PM MAIL WWW ICQ   Вверх
John_Doe
Дата 26.7.2004, 14:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 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), чтобы скрыть внутренности. Можно подумать о паттернах. Можно ещё что-нить придумать tounge.gif

2) Вообще-то, деструкторы могут быть виртуальными, т.е. данный код:

class A
{
public:
virtual void Clear()=0;
~A() {Clear();}
};

Правильнее писать так:

class A
{
public:
virtual ~A()=0;
};

И не удивляться кривизне С++ hehe.gif

PM MAIL   Вверх
achmed
Дата 26.7.2004, 14:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 150
Регистрация: 12.4.2004

Репутация: нет
Всего: нет



класс A это интерфейс, вызвать методы нужно по интерфейсному указателю

Код
virtual void func1(A* obj)


PM MAIL   Вверх
Hroft
Дата 26.7.2004, 14:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 310
Регистрация: 20.10.2003
Где: Москва

Репутация: нет
Всего: 3



Цитата

Если нужно, чтобы потомки обменивались "конфиденциальными" данными, то можно просто определить protected структуру, описывающую эти данные, а сам метод вынести в public.

Дак ведь к членам тоже нельзя защищенным через параметр-указатель пробраться. Так что метод, определенный в потомке, не сможет обратиться к этой структуре.

А еще нельзя вызывать из конструктора чисто виртуальные функции вообще, а просто виртуальные не рекомендуется (хотя по-моему тоже просто нельзя). Причем g++ это отлавливает при компиляции, VC++6.0 при линковке (непонятно как), а Билдер только в рантайме (мол, Pure virtual function called). Но тут понятно - конструктор пытается вызвать функцию с отсутствующим телом, ни о каких виртуальных таблицах он ничего не знает. А вот с деструктором по-идее не так - он вполне может посмотреть по v-таблице, но... error: abstract virtual `virtual void A::clear()' called from destructor, говорит g++. То же, что и с конструктором.
Добавлено @ 14:45
А с указателем то же самое, achmed. Но вообще да, ссылками как-то нехорошо, для них вообще виртуальность будет работать? Я не помню, проверить лень.

PM MAIL ICQ   Вверх
C/L
Дата 31.7.2004, 02:41 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Дело в том, что при создании/удалении потомка вызывается сначала конструктор/деструктор родителя, при чем с VTable родителя. А просто виртуальные функции вызывать можно - всё таки какая-то реализация всё равно будет.
  Вверх
Diaus
Дата 31.7.2004, 07:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 12
Регистрация: 24.7.2004

Репутация: нет
Всего: нет



И всё-таки вопрос, как организовать обмен конфеденциальной информацией между
классами, производными от некоторого базового класса, остаётся открытым. Напомню - это главный вопрос в этой теме.
PM MAIL   Вверх
Guest
Дата 31.7.2004, 20:33 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











Поправляю своё предыдущее сообщение.
Конструкторы и деструкторы классов не наследуются. При создании объекта сначала вызывается конструктор родителя а потом потомка. При удалении наоборот сначала вызывается деструктор потомка потом родителя. Становится понятно почему используется VTable родителя: при создании объекта VTable потомка еще не существует, а при удалении VTable потомка уже оказывается удален.
По поводу доступа protected. Я считаю логичным, что невозможен доступ к защищенным членам объекта другого класса, пусть и произошедшим от одного родителя. Protected создан только для доступа к своим наследуемым членам. То, что нет вида доступа для общения между классами-потомками конечно большое упущение(опущение) Мелкософта. Надо пожаловаться - может в 8 версии VC++ будет.
То, что член защищен не значит, что его нельзя вскрыть. На это способен только извращенский оператор reinterpret_cast, правда ссылку на объект придется заменить указателем. Фактически это предложил Borisff2003, только для данных. А это действует и для виртуальных функций. Да так даже и приват можно вскрыть, я проверял.
Цитата
class A
{
public:
    virtual void func1(A* obj)=0;
protected:
    virtual void func2()=0;
};

class B:public A
{
public:
    virtual void func1(A* obj)
    {
        reinterpret_cast<B*>(obj) -> func2();
    }
protected:
    virtual void func2() {}
};

Как и предупреждал Borisff2003, при использовании reinterpret_cast надо следить, чтобы небыло обращений к членам потомка - компилятор это пропустит, а что сделает получившаяся программа неизвестно.
  Вверх
Олег М
Дата 2.8.2004, 11:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 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(){}
};
Тогда всё будет нормально
Цитата
а что сделает получившаяся программа неизвестно.

Почему? Всё отработает корректно. Мы же только снимаем модификатор "протектед"

PM MAIL ICQ   Вверх
Borisff2003
Дата 2.8.2004, 11:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 198
Регистрация: 26.2.2004
Где: г. Уфа

Репутация: 1
Всего: 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)
   {
       reinterpret_cast<B*>(obj) -> func2();
       reinterpret_cast<B*>(obj) -> func3();//Бздям
   }
protected:
   virtual void func2() {}
   virtual void func3() {}
};


Это сообщение отредактировал(а) Borisff2003 - 2.8.2004, 11:14
--------------------
Лень, двигатель прогресса
PM MAIL WWW ICQ   Вверх
Олег М
Дата 2.8.2004, 11:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 436
Регистрация: 10.6.2004
Где: Москва

Репутация: 7
Всего: 7



Borisff2003

Ясно. Как всегда был невнимателен. Это тоже будет работать корректно, но только если в функ1 будут передаваться указатели на экземпляры класса В или его потомков. В остальных случаях работать нихрена не будет.
А зачем вам вообще все эти извращения? Или так, полёт фантазии?

А что такое "Бздям"?
PM MAIL ICQ   Вверх
C/L
Дата 2.8.2004, 20:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 107
Регистрация: 31.7.2004
Где: Самара

Репутация: 1
Всего: 1



Олег М , хороший выход с классами друзьями.
Но что, если заранее неизвестно сколько и каких будет классов потомков, а исходника реализации класса (*.cpp) нету.
Добавлено @ 20:37
Это я неподумавши написал. Надо же изменить только определение класса, а не реализацию. Но все равно лучше бы был специальный тип доступа, чем каждый раз лезть в *.h файл разработчика класса (если класс не твой).
PM MAIL WWW ICQ   Вверх
C/L
Дата 2.8.2004, 20:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 107
Регистрация: 31.7.2004
Где: Самара

Репутация: 1
Всего: 1



Цитата
Это тоже будет работать корректно, но только если в функ1 будут передаваться указатели на экземпляры класса В или его потомков.

Небольшая поправка, Олег М. С экземплярами класса A (если он не абстрактный) и его потомками тоже будет работать (при условии обращений к членам класса A), я уже проверял. Но предложенный тобою способ конечно лучше.
Цитата
В остальных случаях работать нихрена не будет.

Какие могут быть остальные случаи, если передается именно указатель на объект класса A. Все потомки будут приводится к нему, а другие классы просто не пройдут.
Заранее извиняюсь если я чего-то не понял.

Это сообщение отредактировал(а) C/L - 3.8.2004, 01:55
PM MAIL WWW ICQ   Вверх
C/L
Дата 2.8.2004, 23:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 107
Регистрация: 31.7.2004
Где: Самара

Репутация: 1
Всего: 1



Хотя про friends еще в самом начале писал chipset. Только он не уточнил как именно их использовать.

Это сообщение отредактировал(а) C/L - 3.8.2004, 01:59
PM MAIL WWW ICQ   Вверх
Олег М
Дата 3.8.2004, 06:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 436
Регистрация: 10.6.2004
Где: Москва

Репутация: 7
Всего: 7



Цитата
каждый раз лезть в *.h файл разработчика класса

Это ни вкоем случае делать нельзя. Если нужно вызывать протектед-члены класса - наследуйся от него и вызывай сколько угодно. Для того и сделано.
Цитата
С экземплярами класса A (если он не абстрактный) и его потомками тоже будет работать (при условии обращений к членам класса A), я уже проверял.

Это естественно. Всё говорилось для данного конкретного случая.
Цитата
Хотя про friends еще в самом начале писал chipset.

Ну да. Хотя действительно, выразился он несколько туманно: "Юзай friend, вроде должно помочь.."
PM MAIL ICQ   Вверх
Borisff2003
Дата 3.8.2004, 06:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 198
Регистрация: 26.2.2004
Где: г. Уфа

Репутация: 1
Всего: 1



C/L
Цитата
Какие могут быть остальные случаи, если передается именно указатель на объект класса A. Все потомки будут приводится к нему, а другие классы просто не пройдут.
Заранее извиняюсь если я чего-то не понял.

Но там могут быть потомки классса A в которых нет func3.


--------------------
Лень, двигатель прогресса
PM MAIL WWW ICQ   Вверх
C/L
Дата 4.8.2004, 05:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 107
Регистрация: 31.7.2004
Где: Самара

Репутация: 1
Всего: 1



Цитата
Цитата
каждый раз лезть в *.h файл разработчика класса

Это ни вкоем случае делать нельзя. Если нужно вызывать протектед-члены класса - наследуйся от него и вызывай сколько угодно. Для того и сделано.

Предлагаете что-то типа этого?
Код
class A
{
public:
   virtual void func1(A* obj)=0;
protected:
   virtual void func2()=0;
};

class B:public A
{
public:
   friend class C;
   friend class D;
};

class C:public B
{
public:
   virtual void func1(B* obj);
protected:
   virtual void func2() {};
};

class D:public B
{
public:
   virtual void func1(B* obj);
protected:
   virtual void func2() {};
};

Здорово!!! И овцы сыты и волки целы!!! smile.gif
PM MAIL WWW ICQ   Вверх
Олег М
Дата 4.8.2004, 06:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 436
Регистрация: 10.6.2004
Где: Москва

Репутация: 7
Всего: 7



C/L
Хитро. Я предлагал не совсем это. Но работает. При условии, что класс А не будет абстрактным, в частности func1. А то в классе С func1 это уже совсем другая функция.
PM MAIL ICQ   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0717 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.