Как стать автором
Обновить

Комментарии 166

Кстати, если у кого-то причина минусования опроса, кроме как неприязнь к C++ или ко мне, или плохое настроение..., то интересно было бы узнать.
Лично я минусанул за нечеткий вариант «Нет».
Нехорошо относить туда интерфейсы.
Ясно. Для меня главная цель опроса — примерно взглянуть на цифры, когда множественное наследование было полезно / необходимо / оправдано именно в таком контексте.
Спасибо за объяснение.
Сильно не хватает пункта — «Да — но потом пожалел» или «Да — но, как потом выяснилось, в этом небыло необходимости»

Это подходит, под пункт «Да — так было удобнее».
«Да — но потом пожалел» — может быть и том проекте, где это необходимо :)
Недавно пришлось вот в такой ситуации class X: public QObject, QRunnable
Извините, это троллинг?
Под ситуацией имелось ввиду какого рода проект и, наверно, немного о сущностях.
Извините, думал и так всё понятно. Нужен был класс задачи для фоновой обработки, который можно поместить в QThreadPool (поэтому QRunnable), но с оповещением о завершении (нужен сигнал, поэтому QObject).
Разве такая же задача не решается, например в Java, без помощи множественного наследования?
Вполне решается если выделить сущность class TaskCompletionNotifier: public QObject и агрегировать её внутри класса, унаследованного от QRunnable. Но зачем, только чтобы избежать множественного наследования?..
Я для себя уточняю. Судя по тому, что Вы рассказали, случай больше подходит под «Да — так было удобнее.»
Это все-же необходимость (я тоже проголосовал за «Да — это было необходимо» и тоже вспомнил пример, когда использовал из-за QObject). Но продиктована она чужой архитектурой (Qt). Говорить, как это же реализовать в других языках немного не корректно, т.к. уже есть определенная иерархия классов и используя ее уже строишь свою. Для другого языка, возможно она бы просто была другая.
Понимаю, но все же если посмотреть глубже, то скорее всего разработчики библиотеки сделали это как раз ради удобства. Тесть, если взглянуть глобально, то множественного наследования можно было бы избежать небольшой ценой. У меня не полилось придумать, как отметить, что этого типа случаи следовало бы отнести к пункту #2.
Ради удобства? Как раз наоборот, я бы хотел узнать почему QRunnable не унаследован от QObject.
НЛО прилетело и опубликовало эту надпись здесь
Возможно схвачу минус, но агрегация создает более слабую зависимость, чем наследование, и, как следствие, 1) проще тестируется 2) потенциально повышается степень переиспользования.
Не понятно к чему Вы здесь про агрегацию?
Во-первых, ответил на вопрос «зачем» заданный в комментарии, на который я ответил :) Во-вторых, агрегация это обычная практика в языках, где нет множественного наследования.
Чисто субъективное мнение — множественное наследование не совсем вписывается в ООП, так как технически нарушается принцип единственной ответственности объекта, унаследованного от двух классов (на практике, этот принцип можно технически восстановить, создав интерфейс, включающий интерфейсы всех необходимых для наследования классов, таким образом, определив ответственность; но не смотря на это, как заметил выше, тесты отдельно агрегированных классов все равно получаются проще).
НЛО прилетело и опубликовало эту надпись здесь
Не соглашусь. Агрегация (или композиция) — это, прежде всего, делегирование реализации. Наследование (или имплементация интерфейсов) — это, прежде всего, типизация. Даже в слаботипизированных языках, где наличие метода проверяется лишь при вызове, а полная сигнатура может вообще не проверяться.
НЛО прилетело и опубликовало эту надпись здесь
Таким образом, при иерархии мне нужно 1) протестировать всю реализацию предка 2) протестировать всю реализацию потомка, причем тесты потомка будут вызывать реализацию предка, создаются лишние ненужные вызовы, объемом которых трудно управлять при довольно сложной логике.
Так же, при наследовании B от A невозможно переиспользовать класс B для более чем одной реализации A, при использовании агрегации у нас появляется возможность использовать B с любой, подходящей по интерфейсу, реализацией A. Короче говоря, наследование — сильная связь, агрегация — слабая.
НЛО прилетело и опубликовало эту надпись здесь
1) При агрегации (или композиции) вы разве не должны протестировать всю реализацию членов своего класса, если, конечно, стремитесь к 100% coverage?
2) При агрегации/композиции и прочих делегированиях разве не создаются ненужные вызовы? Или они вас избавляет от необходимости интеграционных тестов?

Для меня главное преимущество агрегации/композиции лишь легкость читаемости и кода, и тестов, но зачастую они обуславливают внедрение лишних (по отношению к функциональности «родительского» класса) тестов и/или увеличению объёма. IoC хрестоматийный пример агрегации, но, афаик, никто не доказал, что он лучше наследования в плане строк кода или времени его выполнения.
Разве я против 100% покрытия? При агрегации достаточно протестировать каждую реализацию с моками на зависимостях, получается просто и выгодно.
Разумеется, не стоит перебарщивать и пытаться агрегировать всё подряд :)
Недостаточно, увы. Иначе не нужны были бы интеграционные и прочие приемочные тесты, всё бы за доли секунды на юнитах прогнали и фиг с ним, что синтаксис SQL-выражения, передающийся БД, неверен — мок же отработал!
При тестировании компонентов я, разумеется, упоминал модульное тестирование, именно так тестируются отдельные компоненты, моков будет достаточно. Интеграционные тесты это следующий этап тестирования, который следует после выполнения юнит-тестов, но они заключаются в корректном поведении связей между компонентами интеграции, как внутридоменных компонентов, так и внешних, например, доступ к БД. Если у вас простые слабо связанные компоненты, простые юнит тесты, у вас будут простые интеграционные тесты.
Для интеграционных тестов не замечал ослабления сложности из-за слабой связанности. Может потому, что у меня сначала идут интеграционные, а потом юнит? Сначала проверяю интерфейсы, а лишь потом реализацию, и то лишь там где интеграционный сливается в цепочку модульных и банальное DRY требует выделить общее поведение в отдельный тест, который чисто случайно оказывается модульным?
А как вы проверяете интерфейсы при интеграционных тестах, если у вас нет работающей реализации, которая проверяется в юнит тестах?
Пишу интеграционный тест (вход-выход приложения на конкретных данных, обычно два входа — пользовательский и БД), который запускаю крайне редко. Затем пишу юнит-тесты в рамках интеграционного с моками и стабами, затем код, заставляющий их пройти, затем фиксы заставлющие пройти и юниты, и интеграционный. Как только последний прошёл ставлю галочку в «чек-листе». Некоторое отступление от канонического TDD, но мне нравится больше, может потому что ближе к «водопаду» или требованиям заказчика, а я как-то воспитан на них. Начинать писать юнит-тесты, когда толком не знаешь, что будет на входе и что должно быть на выходе лично для меня оказалось неэффективно — постоянно приходилось переписывать юнит-тесты из-за ошибок проектирования интерфейсов юнитов.
Немного сумбурно. Почему бы не потратить несколько дней на проектирование архитектуры и интерфейсов, чтобы потом упростить себе жизнь. И тут даже не важно, однофазный проект или многофазный — для каждой фазы есть свои требования, по которым составляется спецификация, делается декомпозиция архитектуры и определяются компоненты и интерфейсы. Если появляются ошибки проектирования интерфейсов — нужно просто сделать еще более детальный анализ всех возможных юз-кейсов.
Нажал «предпросмотр», и понял, что на нытьё больше похоже. Если интересно — скопировал что было в gedit, пока лежит.
IoC в первую очередь полезен для динамически конфигурируемых программ, а при разработке классических статических программ улучшает изменяемость на последующих фазах и увеличивает процент переиспользования кода.
Лично для меня он лишь средство облегчения тестируемости. Ни разу менять, например, систему хранения на лету (да и вообще) не приходилось.
В яве присутвуют интерфейсы, а в плюсах их нету.
QRunnable не чисто абстрактный класс, там реализован reference counting.
1. Что мешает использовать абстрактные классы с чисто виртуальными функциями как интерфейсы?
2. Что мешает создать макрос-маркер.
3. Например в Visual Studio есть ключевое слово __interface
По первому пункту — ничто не мешает, но кроме объявления методов приходится добавлять еще и виртуальный деструктор, что как-то лениво и надоедает.
Интерфейсы в Java (и других языках, в которых их встречал) по сути синтаксический сахар для полностью абстрактных классов. Реализация нескольких интерфейсов (или реализация одного при обычном наследовании) является множественным наследованием.
Абстрактный класс — по сути преусложненный интерфейс с возможностью добавление ненужной и вредной обязательно реализации.
Реализация в абстрактном классе полезна для соблюдения DRY
Нет. Такая реализация ограничивает в способах соблюдения контракта, навязывая заведомо неэффективные средства в одних случаях и дублируя методы-расширения в других.
Если надо сделать несколько похожих реализаций и не повторяться, то лучше сделать базовую реализацию наследником интефейса, а не его частью как в случае абстрактного класса
А если она не ограничивает в данной конкретной ситуации? И кто мешает нам её переопредлить, если в каких-то исключительных случаях всё же ограничивает?

Сделать базовую реализацию наследником интерфейса и от неё наследоваться конкретным классам? В чём разница с абстрактным классом с конкретными методами?
> А если она не ограничивает в данной конкретной ситуации?
Значит крупно повезло с ситуацией. Еще крупнее повезет, если концепция внезапно не поменяется.
Грубо говоря, абстрактный интерфейс заменяет зависимость «реализует» на куда более сильную «является» без малейшей компенсации, внося ответственнейшее и хрупкое наследование туда, где спокойно можно без него обойтись.

> Сделать базовую реализацию наследником интерфейса и от неё наследоваться конкретным классам? В чём разница с абстрактным классом с конкретными методами?
В случае интерфейса базовая реализация не будет мне мешать сделать еще одну на другой базе, другом языке или другой платформе. Базовую реализацию я могу при желании вовне не светить, с абстрактным классом номер не пройдет.
Множественное наследование бывало использовал, но в очень редких случаях — когда исключение лишь подтверждает практику вселенского зла данного подхода. А так только импровизированные интерфейсы.
Почти никогда не использовал. Большинство исключений были с тем, что С++ в отличии, например, от С#, нет методов-расширений. Второй пример — это когда для написания тестов вместо написания десятка классов-моков быстренько пишем один, который наследуется от всех нужных и в 1-2 строки на функцию делает всё, что должен делать мок (это, конечно, идеологически неверно, но быстро и на таком маленьком и не нуждающемся в модификации коде, как мок — эффективно).

Осознанное построение основной структуры классов с множественным наследованием — недопустимо.
Использовал в менеджере потоков, использовал в менеджере памяти, в менеджере задач. Да и много где применяю. Удобный инструмент, позволяет избегать копипасты и не плодить лишних сущностей.
Множественное наследование используется очень редко (но используется).

А вот виртуальное наследование (ака diamond shape) вообще встречаю только на собеседованиях.
О! Спасибо. Сначала виртуальное наследование тоже должно было быть в опросе, но не получилось придумать, как его вписать в тему.
Я использую виртуальное наследование.
Из-за класса atomic_refcounted_t (для использования совместно с boost::intrusive_ptr), который является базовым для многих интерфейсов, реализумых объектом.
У вас кривая архитектура. Вы навязали сначала использование atomic_refcounted_t для интерфейсов (и всех их потомков!), что потом потребовало создавать объекты через intrusive_ptr.

Правильно — выкинуть вообще atomic_refcounted_t из базовых классов, конечные объекты создавать через shared_ptr.
«Вы навязали сначала использование atomic_refcounted_t… что потом потребовало создавать объекты через intrusive_ptr»

Вы угадали с точностью до наоборот. Я «навязываю» использование intrusive_ptr, что потребовало использовать atomic_refcounted_t.
intrusive_ptr — это для объектов, у которых уже есть счётчик ссылок (COM в win32, например). Для «своих собственных» классов — это ошибка дизайна.
«Для “своих собственных” классов — это ошибка дизайна.»
Не могу с вами согласиться. Задачи бывают разные. У меня например используется Си-шная библиотека для диспетчеризации и обработки асинхронных событий. Она накладывает определенные ограничения на дизайн. Там цепочка объектов «источник события»—«обработчик события» с взаимными ссылками друг на друга.

Intrusive_ptr позволяет делать то, что невозможно с shared_ptr.
1) создавать смарт-поинтеры, указывающие на объект, в конструкторе;
2) получать смарт-поинтер из this (а enable_shared_from_this вернет нас к проблеме множественного наследования).

Другой вопрос, нужны ли возможности (1) и (2). В моем случае — нужны.

Дополнительный бенефит — для ряда объектов без изменяемого состояния вся инициализация происходит в конструкторе, это устраняет необходимость в блокировках при многопоточной работе. (Можно конечно дополнительно к конструктору делать метод init(), который должен вызываться до того, как объект станет доступен нескольким потокам, но это отстой.)
Хммм…
Трудно комментировать решения, не видя кода. Но: enable_shared_from_this (как, впрочем, вы знаете) решает и первую и вторую проблемы.

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

Я к тому, что интерфейсы не должны иметь объектной модели, филдов и т.п. А суть в том, что интерфейсы представляют реализацию методов-помощников, например, интерфейс менеджера памяти, вполне может иметь вспомогательные методы, которые пригодятся. Часто сталкивался с ситуацией, когда класс (!) создавал только для того, чтобы определить в некоторых методах default поведение, что уже кардинально меняло классы-наследники так, как в Java они не могут иметь 2 базовых классов.

Я абсолютно не согласен с теми, кто говорит, что методы нельзя определять из-за возможных конфликтов. Во-первых конфликты возникают из-за возращаемых объектов, бросаемых exception. Во-вторых конфликт уже существует если 2 интерфейса имеют одинаковый метод и он не был так задуман.

Мне очень нравится в этом плане подход Scala, насколько я помню там есть вид «агрегации» и конфликты разрешаются в порядке перечисления агрегатов.
Конечно, я против, чтобы в интерфейсах определяли поля, так как это уже определяет имплементацию.
Упс, промахнулся. См ниже.
Возможно, отчасти, от того, что без механизмов Late State Binding в большом количестве случаев интерфейсы с реализациями всё равно толком не применить?
И это очень грустно, что в некоторых языках этого до определённого момента не было, а где-то до сих пор нет, судя по вашему комментарию.
Страуструп привел хороший пример в своей книге — GUI. Используем там.
Использовал стратегии поведения описанные Александреску, но ограниченно. Использовал для UI и для работы с вершинами в DX9.
Использовал во всех проектах связанных с Qt; вынужденная мера, навязанная самой библиотекой.
Ну что значит навязанная? Можно и без него обходится, просто наследоваться от интерфейсов таки удобнее, чем городить сложное агрегирование!
Попробуйте реализовать View из паттерна MVP без множественного наследования в Qt, тут агрегирование не поможет.
Работаю в большой легаси-системе (более 2.5 млн строк кода). Из-за размера системы, из-за того, что некоторые дизайнерские решения были приняты более 10 лет назад, приходится прибегать к различным ухищрениям (в том числе и множественное наследование).
Я использовал много раз. Тут freehabr.ru/blog/cpp/1757.html «Простая и эффективная «сборка мусора» (garbage collection) на C++» например без множественного наследования пришлось бы вводить промежуточный класс, тем самым излишне усложняя код.
Множественное наследование исключительно удобная штука в тех же mix-in в ATL, или как policy были сделаны в локи. Хорошо что иногда что разработчики языка не слушают всяких зануд вроде «дети, множественное наследование — это плохо, п-нятно»
НЛО прилетело и опубликовало эту надпись здесь
Глючная? Глюки? Вы что-то не то делаете.
НЛО прилетело и опубликовало эту надпись здесь
«множественное наследие всего лишь вариант записи инкапсуляции»

Ага, а C++ — всего лишь вариант записи потока ассемеблерных инструкций.
Как вам нравится прототипное наследование кстати?
НЛО прилетело и опубликовало эту надпись здесь
Но прототипное наследование позволяет создать еще больший бардак, чем множественное. Давайте объявим и его глючным?

От delete кстати научились избавляться еще в 60- e.
НЛО прилетело и опубликовало эту надпись здесь
Чем универсальней средство, тем больший бардак оно может создавать. Это аксиома — танки грязи не боятся, а человек пройдёт там, где «И бронепоезд не промчится, Угрюмый танк не проползёт,» («Там, где пехота не пройдёт» — явное искажение действительности, обусловленное, скорее всего, верою в НТР)
НЛО прилетело и опубликовало эту надпись здесь
Нет, учился на кафедре информационно-измерительной техники факультета автоматики и вычислительной техники. Уклон от чисто «мягких» кафедр был прежде всего в мат. статистику. Изучали не столько как датчики конструировать, а сколько как их показания представить — матожидания, СКО и дисперсии, спектральный анализ и корреляция, в общем всякая муть, мало чего общего имеющая с CRUD :) Чтоб ракета до Нью-Йорка или Вашингтона долетела не нужно знать теорию автоматов и алгоритмов ;)
НЛО прилетело и опубликовало эту надпись здесь
Не нужно распознавание образов. ОС (не та, что операционная система, а та, что обратная связь) на основе коэффициента корреляции между картой США и показанием датчиков. Образ есть, а распознавать его не надо, достаточно сравнивать с эталоном. А если США свой ландшафт изменят, то всё равно цель достигнута :)
Множественное наследование — это ржавая бензопила, которая в какой-то момент отрежет вам голову
НЛО прилетело и опубликовало эту надпись здесь
О, а можно ссылочку на цитату Страуструпа, где он говорит, что жалеет о том, что добавил в С++ множественное наследование? Заранее спасибо.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Я думаю, что не увидел пока от вас ссылки на слова Страуструпа. Поиск по хабру ничего не дал, правда он тут весьма кривой.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Спасибо за ссылку.

И согласитесь, «излагать недостатки множественного наследования» и «жалеть, что добавил множественное наследование в язык» — разные вещи.

В параграфе, на который вы ссылаетесь, Страуструп пишет:
People quite correctly say that you don't need multiple inheritance, because anything you can do with multiple inheritance you can also do with single inheritance. <...skipped...> But why would you want to do that? When is it convenient to use the language facilities? When would you prefer a workaround?

I've seen cases where multiple inheritance is useful, and I've even seen cases where quite complicated multiple inheritance is useful.

Так что ни про какое
Он жалел сильно, что добавил на свою голову в С++ эту возможность.
там нету.
НЛО прилетело и опубликовало эту надпись здесь
Т.е. Вы не читали интервью, но при этом делаете ссылки на него?
НЛО прилетело и опубликовало эту надпись здесь
«Пастернака не читал, но осуждаю».
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Ну как-то так, учитывая, что я не профессиональный переводчик, но надеюсь смысл будет понятен.

Люди вполне правильно говорят, что вам не нужно множественное наследование, потому что все что вы можете сделать используя множественное наследование вы можете сделать используя единственное наследование. Более того, вам вообще не нужно наследование потому что вы можете сделать всё без наследования. Вообще-то вам и классы не нужны, потому что вы всё это можете сделать с помощью указателей и структур данных. Но хотите ли вы этого? Когда удобно использовать средства языка? Когда вы предпочтёте обходной путь?

Я видел случаи, где множественное наследование полезно и я даже видел случаи, где достаточно сложное множественное наследование полезно. Вообще, я предпочитаю использовать средства, предоставляемые языком, а не обходные пути.
НЛО прилетело и опубликовало эту надпись здесь
Вы к обращались к его интервью, вообще-то.
НЛО прилетело и опубликовало эту надпись здесь
А в язык с динамической типизацией ассемблер использовать проще? :) Языки высокого уровня разрабатывались как раз для того, чтобы абстрагироваться от ассемблера.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Я знаю английский. Просто я не сижу на хабре круглые сутки чтобы отвечать на ваш троллинг с минимальным лейтенси.
НЛО прилетело и опубликовало эту надпись здесь
Множественное наследование не зло, хотя бы потому что в С++ нет классического ООП.
И замечу, что mixin и пр. это получение плюсов множественного наследования в среде с одиночным наследованием (а вот минусы у подходов разные, но они есть).
Если причиной криво построенной архитектуры вашего приложения вы называете язык и ту данную вам возможность, которой вы воспользовались неправильно — вы делаете что-то не так.
НЛО прилетело и опубликовало эту надпись здесь
Не может быть инструмент — злом, злом являются кривые руки! Само по себе множественное наследование — это хороший инструмент, который надо применять с умом!

Мне всегда смешно, когда появляется фраза, что язык или инструмент, или парадигма, порождают плохой код. Нет, это делают люди, которые его используют!
НЛО прилетело и опубликовало эту надпись здесь
Владельца :) Давно надо поменять.
НЛО прилетело и опубликовало эту надпись здесь
Я про то, что клавиатуру заказчик в подавляющем большинстве случаев не задаёт.

А лично на моем опыте заказчика, требующего «стандартный набор», зачастую можно переубедить, если действительно веришь в преимущества предлагаемой платформы. Беда в том, что для предлагаемых мне задач я могу предложить лишь увеличение срока решения взамен на его потенциальную гибкость и расширяемость. Мой быдлокод на PHP логически работает ни чуть ни хуже, чем мой же на RoR/C#, но разворачивать его я буду на порядки дольше, а снизить рейт могу только в разы, иначе до конца деплоя не доживу :)
НЛО прилетело и опубликовало эту надпись здесь
Я искренне верю, что если вы знаете преимущества предлагаемой заказчику платформы, то вы сможете их ему доказать. Либо эти преимущества ему не важны. Я вот верю в преимущества для разработчика многих отличных от LAMP платформ, но вот в их преимущества для заказчика не верю (некоторые знаю, некоторые — нет, знаю и недостатки). Даже на уровне «разработка дешевле/быстрее» лично у меня всплывает аргумент «развёртывание и администрирование дороже/дольше», учитывая, что администрование это постоянные затраты, а разработка разовые. А раз не верю, то и доказать не могу.
НЛО прилетело и опубликовало эту надпись здесь
Windows NT много популярнее OS/2, от PS/2 только разъёмы для мышек и клав, и то не на каждой мамке (лично на моей только один — и мышу, и клаву подключить не получится кроме как по USB или SATA :D ). Не доказали? :) А ведь даже не «наследование» было, а партнёрство.
Ситуаций, когда его нужно применить (для большей эффективности) намного меньше, чем когда его можно применить. Это относится и ко многим другим вещам, не только к множественному наследованию. А в различных материалах (книги, туториалы, посты и т. п.) чаще освещают плюсы (когда нужно), а не минусы (когда не нужно) тех или иных возможностей, из-за чего у людей складывается впечатление, что их нужно использовать при любом поводе. Для многих возможностей нужно писать в материалах большими красными буквами: «Трижды подумайте, а нужно ли это вам».
Использовал, чтобы подмешивать к классу ортогональную функциональность — сериализацию или что-то в этом роде. Всегда внимательно смотрел на иерархию, чтобы сильно не разрасталась и вылезло боком.
Хотя, я обычно стараюсь избегать множественного наследования, но есть случаи, когда это лучший вариант.
Например, когда, нужно сделать умный указатель на один из классов в иерархии. Умный указатель требует хранения в объекте каких-то данных, вроде счётчика ссылок, и выполнения каких-то действий в конструкторе/деструкторе, вроде увеличения/уменьшения этого счётчика.
Добавлять эту функциональность в корень иерархии, или делать неинтрузивный указатель — лишняя трата ресурсов.
Разделение на «так было удобнее» и «это было необходимо» считаю крайне странным. Языки программирования высокого уровня как раз и созданы для удобства, никакой необходимости их использовать нет, можно всё писать на ассемблере. Это же касается и каких-то инструментов языка, можно обойтись вообще без наследования, но зачем?

НЛО прилетело и опубликовало эту надпись здесь
И как член-класс будет управлять деструктором того экземпляра, чьим членом он стал?
НЛО прилетело и опубликовало эту надпись здесь
Как раз нужно, счётчик-то не просто так увеличивается/уменьшается, когда он дойдёт до нуля, что-то делается, например, уничтожается объект — вызывается деструктор. Конечно же, можно хранить указатель на объект внутри член-класса, но это опять же лишние ресурсы.
Да и явный вызов в конструкторе увеличения счётчика — тоже идея не из лучших, вдруг на работающей атомной станции кто-то решит добавить ещё один конструктор, а про то, что из него непременно нужно что-то вызывать — забудет?
«Конечно же, можно хранить указатель на объект внутри член-класса»
А с указателем это полный аналог virtual base class, только реализованный вручную.
НЛО прилетело и опубликовало эту надпись здесь
Лишних ресурсов не будет при реализации, которую, похоже, имел ввиду mejedi, когда у базового класса кроме счётчика есть виртуальный деструктор. Тогда придётся хранить указатель на его vtable. Но если деструктор обычный, то кроме счётчика ничего хранить не нужно.
Т.к. религия позволяет мне (исключительно по выходным) писать код бесплатно, см. codepad.org/xrXg5ijY. Вариант в SMode1 — с виртуальным деструктором, вариант SMode2 — с обычным.
НЛО прилетело и опубликовало эту надпись здесь
Не ответил, потому что счётчик ссылок увеличивается/уменьшается в конструкторе/деструкторе указателя, а не базового класса. Тут я сглупил. Это лишний раз показывает, что лучше говорить на конкретных примерах, но примера с альтернативной реализацией подобного указателя без наследования я, к сожалению, не увижу.
«Ту же функциональность вы получите, добавив не наследование, а член-класс счётчика ссылок и вызывая его конструктор и деструктор явно...»

Изобразите это в виде кода пожалуйста.
НЛО прилетело и опубликовало эту надпись здесь
«Правила хорошего тона не позволяют мне писать код бесплатно.»
Очень жаль.
А сколько Вам нужно заплатить, чтобы Вы попробовали убедиться в том, что Вы либо неправильно сформулировали свою мысль, либо банально врёте? :)
НЛО прилетело и опубликовало эту надпись здесь
А Вы оплатите мне этот код? :)

Вам же уже написали.

Если Вы предлагаете создавать в качестве члена класса экземляр счетчика ссылок(это тот, что имеет AddRef и Release(и удаляет себя при refcount == 0) иначе расскажите, что это за класс и какой он реализует функционал?) — то очевидно, что удалить родителя без наличия ссылки на него он не сможет.

Если же Вы предлагаете добавлять в класс только переменную со значением числа ссылок на объект и удалять объект будет умный указатель, то чем это отличается от неинтрузивного boost::shared_ptr? Ну кроме глупости в виде (цитирую) «вызывая его конструктор и деструктор явно в конструкторе и деструкторе обрамляющего класса»
НЛО прилетело и опубликовало эту надпись здесь
Вы — не Александреску и не Страуструп. Отвечайте за свои слова самостоятельно.
НЛО прилетело и опубликовало эту надпись здесь
Вы сказали, что не будете писать ни строчки без оплаты, а следующим же комментарием потребовали у собеседника пример кода — логично предположить, что, согласно Вашему принципу, будет предложена и оплата.
НЛО прилетело и опубликовало эту надпись здесь
Тогда берите деньги и за знаки в комментариях — журналисты и писатели ведь не бесплатно работают. Тем более, если Вы считаете себя равным Страуструпу, он книги вряд ли писал исключительно за «спасибо».
НЛО прилетело и опубликовало эту надпись здесь
В принципе я могу писать книги, но вы же скажите, что это мания величия.
В принципе, Вы утверждаете, разбираетесь в CG, в коде, в написании книг, в чем угодно, но подтверждений этому нет, только глупые отмазки, из чего следует очевидный вывод, что фраза «на словах он Лев Толстой, а на деле — *** простой» — про Вас.
НЛО прилетело и опубликовало эту надпись здесь
Вы разговариваете устами Латыниной и Быкова?
НЛО прилетело и опубликовало эту надпись здесь
Тем хотя бы, что на них ссылаются, а на Вас — нет. И в мнительности, граничащей с манией величия, их уличить также сложно.
НЛО прилетело и опубликовало эту надпись здесь
В том, что Вы, будучи не в состоянии написать ни строки кода, ставите себя в один ряд с ними.
НЛО прилетело и опубликовало эту надпись здесь
а мне после вас распутывать эти макароны на работающей атомной станции мало удовольствия.
На какой АЭС вы работаете и в какой должности? Если близко от меня, будет повод для переезда…
НЛО прилетело и опубликовало эту надпись здесь
Из такого опроса невозможно сделать никаких выводов. Это всё равно что опрашивать население планеты «Читали ли вы когда-нибудь хабр?». Большинство ответит «нет», но это не значит что хабр не нужен или сильно плох.
«Проголосовал 1651 человек. Воздержалось 1969 человек.»
А вот из этого можно делать выводы.
Например — С++ теряет актуальность, но все еще вызывает интерес почтенной публики.
А опрос и не говорит, что MI не нужно или сильно плохо.
Существуют приемы, которые реализуются с использованием наследования (в том числе и множественного).
К примеру наследование от интерфейса, который передается в другой класс для обратного вызова.
Или идиома CRTP (Curiously recurring template pattern).
Проблема не в наследовании как таковом, а в отсутствии контроля над архитектурой проекта.

Из книги Александреску «Стандарты программирования на C++ 101 правило и рекомендация»:
32. Ясно представляйте, какой вид класса Вы создаете. (Классы-значения, базовые классы, классы свойства, классы стратегии, классы исключения, классы для RAII)
33. Предпочитайте минимальные классы монолитным. (Разделяй и властвуй)
34. Предпочитайте композицию наследованию.
35. Избегайте наследования от классов, которые не спроектированны для этой цели. (к примеру std::string)
36. Предпочитайте представление абстрактных интерфейсов.
37. Открытое наследование означает заменимость.
Наследовать надо не для повторного использования, а чтобы быть повторно использованным.
Множественным наследованием пользуюсь. В реальных коммерческих проектах, которые поддерживаю годами.
Зачем? Во-первых, частично реализованные интерфейсы (иногда экономит массу кода). Во-вторых, по прямому назначению.
Да, это крайне мощный инструмент, а значит им надо уметь очень аккуратно пользоваться.
Это и неудивительно. С++ весь такой.

Кстати, и виртуальным наследованием также пользуюсь. Тоже стараюсь очень аккуратно.
Когда есть смысл заменить наследование на включение объекта в класс, всегда это делаю. Потому что наследование — гораздо более сильная форма сцепления, чем включение в класс. Если кого интересует подробнее, рекомендую внимательно почитать Голуба.
Скажи, зачем тебе это знать в час ночи 2-ого января?
?

Использовал множественное наследование в ATL. Давно, лет 10 назад, ещё под VC++ 6.0.
Это был собственный интерпретатор.
Извините конечно, но как можно написать что-то сложнее школьной задачки, так чтоб это было с нормально архитектурой и было нормально поддерживаемо без множественного наследования. Особенно когда в C++ нет такой штуки как интерфейсы, то вообще непонятно как так работать можно.
Там же в опросе написано — «наследование от интерфейсов» множественным наследованием не считаются.

Да, и в С++ интерфейсами традиционно считаются «пустые» классы с абстрактными виртуальными функциями.
Сорри не увидел. Про традиционную форму реализации интерфейсов в курсе.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории