LINUX.ORG.RU

В чём суть ООП?


3

5

С точки зрения манеры проектирования, а не особенностей реализации С++ там CLOS. Вот например я использую определения классов и даже иногда наследование как узнать пишу ли я в ООП стиле?

★★★★★

Последнее исправление: ados (всего исправлений: 1)

Ответ на: комментарий от Begemoth

1. Что такое низкоуровневый вывод?

форматированный вывод встроенных типов, таких как int, double. Выигрыша от ООП тут нет, за то накладные расходы велики.

в FILE ООП в полный рост.

ИМХО FILE это НЕ ООП.

drBatty ★★
()
Ответ на: комментарий от Boy_from_Jungle

подразумевается, что в ~Bar() есть удаление, а в ~Foo() нету. Потому этот код и приведёт к утечке, если ~Foo() сделают НЕ виртуальным. Проблема в том, что ~Foo() может быть написан 10 лет назад, когда numbers и в проекте не было, но сделать его виртуальным можно было уже тогда.

И да, это просто пример, причём не я автор. Но пример жизненный.

drBatty ★★
()
Ответ на: комментарий от Boy_from_Jungle

Здесь вроде все просто, если класс не имеет виртуального деструктора - он НЕ предназначен для наследования.

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

Begemoth ★★★★★
()
Ответ на: комментарий от son_of_a_gun

Объект не должен решать _куда_ ему выводиться, только _как_. ostream - направление вывода operator<< - способ вывода. Что некошерного то?

вообще говоря, всё зависит от задачи. _как_ вообще-то подразумевает _куда_. Т.е. кошерно x->output(); т.к. тут нет никаких лишних сущностей. Возможно x->output(«FORMAT_STRING»); если нужен форматированный вывод.

drBatty ★★
()
Ответ на: комментарий от drBatty

Но пример жизненный.

А можно привести пример из жизни, когда дейсвительно потребовалось унаследоваться от неполиморфного класса?

Begemoth ★★★★★
()
Ответ на: комментарий от drBatty

_как_ вообще-то подразумевает _куда_

С чего бы? _Как_ подразумевает прорядок преобразования величины в последовательность символов, а _куда_ эти символы будут записаны и как преобразованы в байты - забота уже другого компонента.

Begemoth ★★★★★
()
Ответ на: комментарий от jtootf

давай ты прочитаешь хотя-бы одного из них:

Virtual Question #2: What About Base Class Destructors?

The second classic question we'll consider is that old destructor chestnut: «Should base class destructors be virtual?»

Sigh. I wish this were only a frequently asked question. Alas, it's more often a frequently debated question. If I had a penny for every time I've seen this debate, I could buy a cup of coffee. Not just any old coffee, mind you - I could buy a genuine Starbucks Venti double-Valencia latte (my current favorite). Maybe even two of them, if I was willing to throw in a dime of my own.

The usual answer to this question is: «Huh? Of course base class destructors should always be virtual!» This answer is wrong, and the C++ standard library itself contains counterexamples refuting it, but it's right often enough to give the illusion of correctness.

The slightly less usual and somewhat more correct answer is: «Huh? Of course base class destructors should be virtual if you're going to delete polymorphically (i.e., delete via a pointer to base)!» This answer is technically right but doesn't go far enough.

I've recently come to conclude that the fully correct answer is this:

Guideline #4: A base class destructor should be either public and virtual, or protected and nonvirtual.

(выделено автором)

Теперь обрати внимание, что я говорил лишь о публичных деструкторах, Джефф - тоже, и в твоей ссылке также говорится о том, что публичные деструкторы должны быть виртуальными. Что касается защищённых, то и Джефф и я, с сожалением вынужденны делать их НЕ виртуальными.

Т.ч. никакого противоречия нет, и не было.

drBatty ★★
()
Ответ на: комментарий от drBatty

вообще говоря, всё зависит от задачи.

И в данной задаче объект не должен знать куда он отдает вывод.

_как_ вообще-то подразумевает _куда_

Нет. Я могу определить operator<< для своего BigInt - и мне не нужно беспокоиться куда его выведут: cout, cerr, в stringstream какой-нибудь, fstream.

Т.е. кошерно x->output(); т.к. тут нет никаких лишних сущностей

Наоборот - некошерно, ибо оладает гораздо меньшими возможностями.

son_of_a_gun
()
Ответ на: комментарий от LamerOk

1. Состояние / идентичность объектов (и инкапсуляция сюда же) - да. 3. Сигналы / методы / сообщения (синхронные и нет) с полиморфизмом / диспетчеризацией по типам объектов - тоже правильно. 2. Интерфейсы, если понимать «каждый из объектов принадлежит некоторому классу объектов» как «для объекта этого типа реализован такой-то интерфейс, следовательно, с этим объектом можно общаться средствами этого интерфейса». При этом интерфейсов у типа может быть много, в C++ это будут как родные методы класса (геттеры с сеттерами, например), так и множественное наследование от «полнокровных» и чисто виртуальных классов (вместо чистых интерфейсов). 4. Ну и подтипы с выполнением LSP.

X Winodws System, WinAPI и Qt

+ CLOS (в виде Tiny-CLOS / PCL от Xerox).

quasimoto ★★★★
()
Ответ на: комментарий от Begemoth

А как же fmemopen?

если-бы это было ООП, достаточно было-бы просто fopen(). В классе FILE::MEMORY её можно было-бы перезагрузить, и клиент даже-бы и не узнал, что файл в памяти (если это ему не надо, а обычно - не надо).

drBatty ★★
()
Ответ на: комментарий от drBatty

если-бы это было ООП, достаточно было-бы просто fopen()

fopen - это конструктор типа FILE::FileSystemStream, на не фабрика.

В классе FILE::MEMORY её можно было-бы перезагрузить, и клиент даже-бы и не узнал, что файл в памяти

1. В дочернем классе перегрузить фабричный метод?

2. Клиент - это код получающий FILE*, fprintf, например.

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

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

в общем случае - да. Но я - ладно. Многие тут полагают, что для костылестроения должен быть пригоден класс Y, который вообще ничего общего с X не имеет, кроме некоторых похожих методов. Вот так, к примеру:

<?php
class Duck{
        function quack(){ echo "Quack\n";}
        function fly(){ echo "Flap, Flap\n";}
}
class Person{
        function __construct($name){$this->name=$name;}
        function quack(){echo "{$this->name} walks in the forest and imitates ducks to draw them\n";}
        function fly(){ echo "{$this->name} takes an airplane\n";}
}
 
function QuackAndFly($obj){$obj->quack(); $obj->fly();}
 
QuackAndFly(new Duck());
QuackAndFly(new Person("Jules Verne"));
 
/*
Output
``````
Quack
Flap, Flap
 
Jules Verne walks in the forest and imitates ducks to draw them.
Jules Verne takes an airplane.
*/
(за код бить морду википедии, это не я) Т.ч. моя позиция далеко не так радикальна...

drBatty ★★
()
Ответ на: комментарий от Begemoth

А можно привести пример из жизни, когда дейсвительно потребовалось унаследоваться от неполиморфного класса?

я же уже говорил - коллекция, которая умеет удалять некие объекты Foo. Если нам нужно тоже Foo но с бантиком, то мы можем унаследовать Bar от Foo, вот только когда новый Foo будет удаляться, бантик зависнет.

drBatty ★★
()
Ответ на: комментарий от quasimoto

И ещё про Glib, Objective-C и Java нужно не забыть.

Все трое не имеют прямого отношения к модели ООП. Если уж «не забывать», то скорее Erlang и микроядра.

LamerOk ★★★★★
()
Ответ на: комментарий от Begemoth

С чего бы? _Как_ подразумевает прорядок преобразования величины в последовательность символов, а _куда_ эти символы будут записаны и как преобразованы в байты - забота уже другого компонента.

ты голову включи: _как_ должно быть приколочено к _типу_ объекта вывода - целые числа мы выводим одним способом, дробные другим, а свой класс - третьим. Приколачивать скажем к консоли формат скажем «%03.7f» лично мне видится бредом. А именно это и предлагают авторы iostream. Если мне нужен скажем 24х часовой формат времени, то я его и приколочу к объекту MY_TIME, но никак не к объекту cout. Потому-что, мне пофиг, _куда_ выводится MY_TIME, мне важно, что-бы в 24х часовом формате. А тебе, и создателям cout не важно, что это за MY_TIME, им важно, что-бы в консоль какие-то байты непонятно откуда выводились как время в 24х часовом формате. А на принтер - AM/PM.

drBatty ★★
()
Ответ на: комментарий от Begemoth

давай ты прочитаешь хотя-бы одного из них по внимательнее: What About Base Class Destructors?

что? я прочитал. Тебе перевести? Текст-то несложный...

drBatty ★★
()
Ответ на: комментарий от drBatty

Приколачивать скажем к консоли формат скажем «%03.7f» лично мне видится бредом. А именно это и предлагают авторы iostream

В каком месте они это предлагают? Ты про std::istream и std::ostream вообще вкурсе?

Begemoth ★★★★★
()
Последнее исправление: Begemoth (всего исправлений: 1)
Ответ на: комментарий от son_of_a_gun

И в данной задаче объект не должен знать куда он отдает вывод.

да. А почему этим тогда занимается operator<< того, _куда_ выводится? Этим, ИМХО, должен заниматься оператор того, _что_ выводится, а именно x->output(), но никак НЕ cout.operator<<(x). На кой леший консоли или файлу, или непойми чему разъяснять, _как_ и _что_ выводить? А именно это ты делаешь, когда перезагружаешь operator<<().

Нет. Я могу определить operator<< для своего BigInt - и мне не нужно беспокоиться куда его выведут: cout, cerr, в stringstream какой-нибудь, fstream.

ты лучше определи метод для своего bigint, что-бы переводил его в поток байтов, например в std::string. Это более нужная фича, чем вывод его куда-то.

Наоборот - некошерно, ибо оладает гораздо меньшими возможностями.

да ладно! x->output() выводит что угодно и куда угодно. А твой cout.operator<<() очевидно только в cout, да и то, только если есть нужный operator<<().

drBatty ★★
()
Ответ на: комментарий от drBatty

operator<< того, _куда_ выводится?

С чего ты решил, что это так? Это свободная функция, а не член класса, усналедованного от std::ostream.

А твой cout.operator<<() очевидно только в cout

Ты специально стандарт перевираешь?

Begemoth ★★★★★
()
Последнее исправление: Begemoth (всего исправлений: 1)
Ответ на: комментарий от Begemoth

fopen - это конструктор типа FILE::FileSystemStream, на не фабрика.

таки и конструктор и фабрика. Ибо объект FILE при этом не только создаётся, но и инициализируется в соответствии с именем и с режимом.

1. В дочернем классе перегрузить фабричный метод?

не знаю. Говорил уже - ООП тут ИМХО не нужно. Считаешь иначе? Ну сделай мне что-ли ООП либу для файлов... Посмотрим...

2. Клиент - это код получающий FILE*, fprintf, например.

да. Вот и реализовывай. Мне это не нужно.

drBatty ★★
()
Ответ на: комментарий от drBatty

я говорил лишь о публичных деструкторах

можно ссылку на комментарий в котором ты это уточнил? grep меня что-то подводит

jtootf ★★★★★
()
Ответ на: комментарий от LamerOk

Это ты не мне, это ты Алану Кею заливай. ))

о, да с удовольствием

jtootf ★★★★★
()
Ответ на: комментарий от drBatty

В общем плюсы ты не знаешь. Ну так бы и сказал. Во первых cout - это не класс. Во вторых - потоки решают пролему буферизации, не придется копировать/создавать временное представление.

son_of_a_gun
()
Ответ на: комментарий от drBatty

когда речь идёт о низкоуровневом выводе (как в printf(3)), то ООП просто не нужно. О чём я и говорю уже третий день.

Ты заблуждаешься, тебе об этом 3 день говорят.

Dudraug ★★★★★
()
Ответ на: комментарий от Begemoth

уточню, пример использования такой коллекции.

в принципе всё что угодно. Взять те же файлы - когда программа отработала, система обязана закрыть все незакрытые файлы. Если у тебя открыты какие-то свои файлы, то либо закрывай сам, либо вноси изменения в систему. Но. Если-бы система была-бы у нас ООПшная, то мы могли-бы унаследовать MyFILE от FILE системы. Это привело-бы к том, что система закрыла-бы не только свои, но и наши файлы, используя наш деструктор. А так - она конечно закроет, но только своим деструктором. Т.е., ООП позволит нам переопределить fclose(3), попросту унаследовав MyFILE от FILE.

drBatty ★★
()
Ответ на: комментарий от Begemoth

А твой cout.operator<<() очевидно только в cout

Ты специально стандарт перевираешь?

что, cout.operator<<() может выводить не в cout? А куда?

drBatty ★★
()
Ответ на: комментарий от Begemoth

Уже сделана - glibc.

не наблюдаю там не инкапсуляции (struct FILE), ни полиморфизма (возможности переопределить скажем fopen/fclose)

drBatty ★★
()
Ответ на: комментарий от son_of_a_gun

В общем плюсы ты не знаешь. Ну так бы и сказал.

ты - тем более.

Во первых cout - это не класс.

я знаю. А что? cout это объект класса ostream, если тебе интересно проверить мои знания. И?

Во вторых - потоки решают пролему буферизации, не придется копировать/создавать временное представление.

вот эта проблема как раз и должна решаться внутри cout, в отличие от проблемы форматного вывода. Не уводи обсуждение в сторону - против буферизации ещё никто не выступал.

drBatty ★★
()
Ответ на: комментарий от Dudraug

когда речь идёт о низкоуровневом выводе (как в printf(3)), то ООП просто не нужно. О чём я и говорю уже третий день.

Ты заблуждаешься, тебе об этом 3 день говорят.

ага. Только в чём я заблуждаюсь, никто не знает. Дошли уже до того, что у нас FILE это ООП...

drBatty ★★
()
Ответ на: комментарий от drBatty

я знаю. А что? cout это объект класса ostream, если тебе интересно проверить мои знания. И?

Да? Тогда, походу, ты не знаешь что это значит.

вот эта проблема как раз и должна решаться внутри cout, в отличие от проблемы форматного вывода. Не уводи обсуждение в сторону - против буферизации ещё никто не выступал.

Что мешает сделать свой метод, принимающий формат и ссылку на ostream? Универсальности то тут и так нет.

son_of_a_gun
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.