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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Абстрактный класс и operator << 
:(
    Опции темы
T0ohtik
Дата 19.7.2008, 01:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Доброй ночи, товарищи! Возникла задача написать базовый, абстрактный класс. И перегрузить опретацию <<. Получается, что для достижения этой целин надо написать виртуальную, дружественную, нулевую функцию. Возможно ли такое или я что то нагородил?
PM MAIL   Вверх
vinter
Дата 19.7.2008, 08:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

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



делаешь виртуальный\чисто виртуальный оператор в базовом классе, например +=, потом переопределяешь его в потомке. И делаешь внешнюю ф-ию например +, и все..


--------------------
Мой блог
PM MAIL WWW   Вверх
Annihilator
Дата 19.7.2008, 09:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


bytegrinder
**


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

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



Попробуй так
Код

class ClassA
{
    virtual ostream& out(ostream& os) const = 0;
    friend ostream& operator<<(ostream& os, const ClassA& a) {
        return a.out(os);
    }
};

class ClassB1 : public ClassA
{
    ostream& out(ostream& os) const {
        os << "ClassB1::out(ostream& os)";
        return os;
    }
};

class ClassB2 : public ClassA
{
    ostream& out(ostream& os) const {
        os << "ClassB2::out(ostream& os)";
        return os;
    }
};


Это сообщение отредактировал(а) Annihilator - 19.7.2008, 09:53


--------------------
Если вы не можете сделать хоpошyю пpогpаммy, сделайте, чтобы она по кpайней меpе выглядела хоpошо
PM ICQ   Вверх
Torsten
Дата 19.7.2008, 11:19 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



У меня почти такой же вариант, как и у Annihilator, только оператор << не дружественная функция.

Код

class A
{
public:
    virtual ~A() {}
    virtual std::ostream & Print(std::ostream & strm) const = 0;
};

std::ostream & operator<<(std::ostream & os, const ClassA & a)
{
       return a.Print();
}


Это сообщение отредактировал(а) Torsten - 19.7.2008, 11:25
--------------------
We have no begining, we have no end. We are infinite.
PM MAIL   Вверх
Ulysses4j
Дата 19.7.2008, 19:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Я за версию Annihilator, хотя версия Torsten соответствует тому, что у Мейерса. Мотивация такая: out не предназначена для внешних пользователей — клиенты этой иерархии классов должны использовать operator<< — но есть одно исключение, сам operator<<: это значит, надо делать out закрытой, а operator<< френдить.

Но вместо out лучше print, да (со строчной буквы!).


--------------------
Communication is critical to the job of a programmer.
C. Jazdzewski. Fatherly Advice To New Programmers
PM MAIL WWW   Вверх
vinter
Дата 19.7.2008, 21:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

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



Цитата(Ulysses4j @  19.7.2008,  20:00 Найти цитируемый пост)
хотя версия Torsten соответствует тому, что у Мейерса

Насколько я помню Майерс за инкапсуляцию, а френды ее нарушают. Вообще френды надо юзать при крайней необходимости, я считаю.


--------------------
Мой блог
PM MAIL WWW   Вверх
Ulysses4j
Дата 19.7.2008, 22:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(vinter @  19.7.2008,  22:34 Найти цитируемый пост)
 Майерс за инкапсуляцию

 smile Понимаете, все мы за инкапсуляцию: я, вы, Мейерс — но френдов время от времени используют даже “великие”. Просто Мейерс реализовал так и почему-то никак своего решения не объяснил (это, если вдруг интересно, More Effective, 25). Я пояснил, почему на мой взгляд открыть print в данном случае плохо: это, если непонятно, еще более очевидным образом нарушает инкапсуляцию. 

Есть еще такая мотивировка. Изначально operator<< хочется сделать членом класс (так и Мейерс, кстати, начинает рассказывать, но это очевидно и из общих соображений: функция вывода в поток должна иметь доступ к представлению, чтобы его непосредственно и выводить). Но если поступить так, то не получится реализовать привычную идиому os << foo; (придется задом наперед). Вот и идут на этот финт с print. Но если вы изначально хотели предоставить operator<< доступ к реализации, то чего уж бояться делать его френдом: ведь член это более близкая связь чем френд.

С print есть еще проблема: пользователь класса будет смотреть и видеть две функции одного назначения: print и operator<<. Это ненужное раздувание интерфейса, умножение сущностей без необходимости (привет Оккаму). В случае с закрытой print для пользователя есть только одна функция выполняющая одну работу. Все предельно ясно.

И все-таки меня терзают смутные сомнения, что я где-то читал вариант с закрытой print. Что за память...


--------------------
Communication is critical to the job of a programmer.
C. Jazdzewski. Fatherly Advice To New Programmers
PM MAIL WWW   Вверх
UnrealMan
Дата 19.7.2008, 23:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(Ulysses4j @  19.7.2008,  22:16 Найти цитируемый пост)
С print есть еще проблема: пользователь класса будет смотреть и видеть две функции одного назначения: print и operator<<. Это ненужное раздувание интерфейса, умножение сущностей без необходимости (привет Оккаму). В случае с закрытой print для пользователя есть только одна функция выполняющая одну работу. Все предельно ясно.

Однако, тот, кто наследуется, должен знать о существовании print у базового класса. Т.е. print должна быть задокументирована.
PM MAIL   Вверх
Ulysses4j
Дата 19.7.2008, 23:57 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Да, дельное замечание. Под клиентами я, разумеется, понимал классы, непосредственно использующие объекты рассматриваемого класса (с “виртуальной” функцией вывода в поток) и его наследников. Все-таки по умолчанию я предполагаю, что человек не наследуется от классов чужой библиотеки (наследование реализации многажды поруганная стратегия... впрочем, как и френдизм smile). А в рамках одной библиотеки человек/команда проектирует иерархию классов со всеми виртуальностями и должны, конечно, быть осведомлены об используемом механизме.


--------------------
Communication is critical to the job of a programmer.
C. Jazdzewski. Fatherly Advice To New Programmers
PM MAIL WWW   Вверх
Torsten
Дата 20.7.2008, 00:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



Цитата(Ulysses4j @  19.7.2008,  22:16 Найти цитируемый пост)
Я пояснил, почему на мой взгляд открыть print в данном случае плохо: это, если непонятно, еще более очевидным образом нарушает инкапсуляцию. 

И чем же она нарушает инкапсуляцию ? Тем что выводит свои данные в поток ? Ее обычно и создают, чтобы выводить информацию, а не скрывать ее.


Цитата(Ulysses4j @  19.7.2008,  22:16 Найти цитируемый пост)
С print есть еще проблема: пользователь класса будет смотреть и видеть две функции одного назначения: print и operator<<. Это ненужное раздувание интерфейса, умножение сущностей без необходимости (привет Оккаму). В случае с закрытой print для пользователя есть только одна функция выполняющая одну работу. Все предельно ясно.

Можно создать класс printable, у которого будет переопределен оператор << и вызывается виртуальная функция print. Унаследованный класс должен будет переопредилить функцию print, и в тоже самое время ему не нужно будет определять оператор <<, т.к. он реализован для класса printable. Когда пользователь будет смотреть архитектуру класса он не будет видеть лишнего и сам интерфейс не будет раздуватся, т.к. достаточно будет в проивзодных классах определить только 1 функцию.


Цитата(Ulysses4j @  19.7.2008,  22:16 Найти цитируемый пост)
Есть еще такая мотивировка. Изначально operator<< хочется сделать членом класс (так и Мейерс, кстати, начинает рассказывать, но это очевидно и из общих соображений: функция вывода в поток должна иметь доступ к представлению, чтобы его непосредственно и выводить). Но если поступить так, то не получится реализовать привычную идиому os << foo; (придется задом наперед). Вот и идут на этот финт с print. Но если вы изначально хотели предоставить operator<< доступ к реализации, то чего уж бояться делать его френдом: ведь член это более близкая связь чем френд.

Это мнение ошибчно. Для того, чтобы это понять нужно понять "принцип интерфейса". 
В случае ostream & operator <<(ostream & strm, const Foo & foo) { /*код для вывода в поток */} в соотвествии с принципом интерфейса.
Так как operator << упоминает Foo - он является логической часть Foo. 
operator << упоминает ostream, поэтому operator << зависит от ostream.
operator << является логической частью Foo и зависит от ostream, следовательно Foo зависит от ostream. 

Таким образом реализация посредством френд оператора <<, ничем не лучше функции Print. А Print как я уже и писал выше имеет преимущество перед френд оператором в том, что в поток корректно выводтся все производные от X классы, даже при передаче оператору << ссылки производных классов.

Цитата
Но вместо out лучше print, да (со строчной буквы!).

Это вопрос стиля и правил разработки принемаемых перед созданием проекта. 

Это сообщение отредактировал(а) Torsten - 20.7.2008, 00:46
--------------------
We have no begining, we have no end. We are infinite.
PM MAIL   Вверх
Ulysses4j
Дата 20.7.2008, 01:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(Torsten @  20.7.2008,  01:45 Найти цитируемый пост)
И чем же она нарушает инкапсуляцию ? Тем что выводит свои данные в поток ? Ее обычно и создают, чтобы выводить информацию, а не скрывать ее.

Нет, не тем что выводит данные в поток, а тем, что создавалась она только для использования в operator<<, а а выложив ее в public, ей смогут пользоваться клиенты класса. Ее создают не для того, чтобы выводить данные в поток — видимо, вы позабыли, что print создавалась, чтобы задействовать механизм виртуальности, это деталь реализации operator<<, роль которого как раз вывод в поток — с точки зрения клиента.

Цитата(Torsten @  20.7.2008,  01:45 Найти цитируемый пост)
Print как я уже и писал выше имеет преимущество перед френд оператором в том, что в поток корректно выводятся все производные от X классы, даже при передаче оператору << ссылки производных классов.

Вы не понимаете: это не преимущество print, а ее назначение: обеспечивать виртуальность. Если бы она этого не делала, обошлись бы без нее. Ну, или без operator<<. Если уж решили его сделать, то он должен быть единственным средством вывода в поток.

“Принцип интерфейса” не осилил  smile

Еще один аргумент закрытия print это совет из Стандартов кодирования Саттера&Александреску, повторяемый у Саттера в More Exceprtional и Дьюхерста в Готчах (или если по хронологии, то наоборот, кажется): делайте виртуальные функции закрытыми (идиома non-virtual interface, NVI). В данном случае невиртуальный интерфейс это operator<<, а спрятанная по канонам Саттера-Александреску-Дьюхерста виртуальность это print.


--------------------
Communication is critical to the job of a programmer.
C. Jazdzewski. Fatherly Advice To New Programmers
PM MAIL WWW   Вверх
Torsten
Дата 20.7.2008, 21:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



Цитата(Ulysses4j @  20.7.2008,  01:20 Найти цитируемый пост)
Нет, не тем что выводит данные в поток, а тем, что создавалась она только для использования в operator<<, а а выложив ее в public, ей смогут пользоваться клиенты класса.


Ну и что дальше ? Ну могут они данные вывести, но данные изменять не могут и кроме того они понятие не имеют о том что выводится, так что здесь все нормально.


Цитата(Ulysses4j @  20.7.2008,  01:20 Найти цитируемый пост)
Вы не понимаете: это не преимущество print, а ее назначение: обеспечивать виртуальность. Если бы она этого не делала, обошлись бы без нее. Ну, или без operator<<. Если уж решили его сделать, то он должен быть единственным средством вывода в поток.

Ну если вы не понимаете, тогда я обьясню на коде.
Код

class Message
{
public:
      vritual ~Message() {}
...
};

ostream operator << (ostream & strm, const Message & Msg) { ... }

class SystemMessage : public Message {...};
ostream operator << (ostream & strm, const SystemMessage & Msg) { ... }

class UserMessage : public Message {...};
ostream operator << (ostream & strm, const UserMessage & Msg) { ... }

Message * pSystem = new SystemMessage;
Message * pUser = new UserMessage;

cout << pSystem << pUser ;


Будет использоватся оператор вывода для абстрактного класса Message.
Если реализовывать с помощью функции Print, то во-первых не нужно будет писать для каждого класса оператор <<, а во-вторых самое главное, всегда будут выводится реальные данные, то есть SystemMessage и UserMessage, т.к. функция Print будет вызвана у них.
--------------------
We have no begining, we have no end. We are infinite.
PM MAIL   Вверх
Ulysses4j
Дата 20.7.2008, 21:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(Torsten @  20.7.2008,  22:31 Найти цитируемый пост)
Ну и что дальше ? Ну могут они данные вывести, но данные изменять не могут и кроме того они понятие не имеют о том что выводится, так что здесь все нормально.

Так вы полагаете что инкапсуляция означает невозможность изменить данные? Ну, спешу вас огорчить, это намного более общее понятие: инкапсуляция подразумевает сокрытие реализации класса и его артефактов (в C++ — в частности, тесно связанных с классом свободных функций). Я надеюсь, вы когда-нибудь использовали private-методы. Это тоже инкапсуляция. Надеюсь, когда-нибудь использовали private-константы внутри класса: их можно было бы открыть, потому что их изменить нельзя (хаки с const-кастом не берем, можно рассмотреть другие ОО-языки, в которых подобных cast-ов нет). Однако, закрытые константы используются. Это тоже инкапсуляция.

И скрытие деталей того, как виртуализирована operator<<, то есть закрытие print, это тоже инкапсуляция.

Цитата(Torsten @  20.7.2008,  22:31 Найти цитируемый пост)
Ну если вы не понимаете, тогда я объясню на коде.

О! Вы попытались объяснить мне идиому виртуальной свободной функции, как она описана у Мейерса (см. мою ссылку выше)! smile  Чтоб я б я без вас делал...


Это сообщение отредактировал(а) Ulysses4j - 20.7.2008, 21:49


--------------------
Communication is critical to the job of a programmer.
C. Jazdzewski. Fatherly Advice To New Programmers
PM MAIL WWW   Вверх
T0ohtik
Дата 20.7.2008, 22:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Извините, что долго отсутствовал. Спасибо всем кто ответил!
PM MAIL   Вверх
UnrealMan
Дата 21.7.2008, 12:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(Ulysses4j @  19.7.2008,  23:57 Найти цитируемый пост)
наследование реализации многажды поруганная стратегия... 

Те, кто её ругают, идут лесом. Делая Print закрытой, мы лишаем пользователя возможности осуществлять невиртуальный вызов для базового подобъекта.
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
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.0675 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


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

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