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

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

Товарищ, вы — монстр :)
Огромное спасибо за эту статью — с ней можно намного легче разбираться в коде Qt. Помнится, я сколько раз пытался понять, что значит этот Q_D, но так и не догнал :)
Вроде как, там был еще забавный макрос Q_Q, который похож на плачущий смайлик.
Спасибо, старался.
По поводу Q_Q — смотри пункт 9 — там он описан (Q-указатель (он же Q-pointer)).
Когда ковырял QTabWidget обратил внимание на всю эту кухню. Сейчас в Кутиме для синглтонов похожие штуки юзаем, но только в несколько упрощенном виде.
ИМХО идеальный паттерн для сигнлтонов
Вот тут не очень понимаю? какое преимущество в реализации синглетонов это дает?
Вот в implicit sharing например понятно: создаем копию текущего класса, а в качестве приватного берем указатель (вернее шаред указатель) на экземпляр того от которого копируемся, и держим до тех пор, пока не был вызван метод модификации, вот тогда происходит реальное копирование. Вот тут «кота» в руки, так сказать. А вот как с синглетону это поможет?
Ну чтобы скрыть от плагинов всё, о чем им знать нельзя.
хм. Вариант кстати. А если его сделать шаред. то вообще можно получить самоудаляемый синглетон.
Нада подумать на эту тему. Для малоресурсных систем достаточно актуально.
Типа не нуждается в нем никто — с глаз долой из памяти вон! :-)
Сделаю брейншторм на эту тему, может хорошый подпаттерн появится :-)
Аха, можно и так. Идея хороша, а так как Qt часто работает в условиях ограниченных ресурсов, то будет полезно. Кстати в Qt 4.6 еще ряд хороших указателей появился, например QScopedPointer

The QScopedPointer class stores a pointer to a dynamically allocated object, and deletes it upon destruction.

Managing heap allocated objects manually is hard and error prone, with the common result that code leaks memory and is hard to maintain. QScopedPointer is a small utility class that heavily simplifies this by assigning stack-based memory ownership to heap allocations, more generally called resource acquisition is initialization(RAII).

QScopedPointer guarantees that the object pointed to will get deleted when the current scope dissapears.

doc.trolltech.com/4.6-snapshot/qscopedpointer.html
Т.е. они изобрели auto_ptr/scoped_ptr?
они много поинтеров изобрели. Их около 6 штук :-)
>В дальнейшем я вам расскажу еще немного интересных вещей:
что такое QFlag, в чем его преимущество и что с ним едят.

Обожаю эту штуку, особенно в инкарнации QFlags и макроса Q_DECLARE_FLAGS :)
Можно резво минимизировать использование памяти и время на поиск и улучшить читабельность прог

у него еще есть ряд полезностей, особенно для кросс-платформенной рзаработки. Найду время — обязательно напишу.
да и плюс многие кто переходит с С++ на кьют (у нас в конторе это все «новенькие» :-) ибо на рынке труда практически нет кьют специалистов) начинают выдумывать велосипеды :-) А был один смешной кадр, он вообще заявил «без буста кодить нельзя», а когда я ему сказал что у нас на таргет платформы СТЛя нету, он был ошарашен и не мог понят как же ему теперь писать код :-) Ну в догонку можно сказать что было у него еще заявление такого рода «const — это балавство», после того как я у него поинтересовался, почему же он нехороший человек поубирал const из методов. Типа не собиралось у него :-)
Const баловство? О.о я видимо что-то непонял в этой жизни, это блин как минимум спасатель от глупых ошибок
Что это за платформа, где есть Qt, но нету STL?
это встраиваемые системы. Можно засунуть туда СТЛ, но в нашем случае он там не нужен.Там и так счет идет не на мегабайты а на сотни килобайт. Сто килобайт плюс — уже нужны внятные пояснения почему.
поэтому вы используете Qt? странный конечно выбор…
да именно поэтому. Все Qt библиотеке на устройстве у нас весят окол 5 метров.
Есть еще варианты реализовать портируемую платформу для графического интерфейса без Иксов (нереально большой оверхед дают даже TinyX  и подобные ему)?
А ну и чтоб это влазило в 5 метров?
С учетом того что один сам по себе libstdc++ весит порядка полутора метров. Я молчу об возможных прослойках (DFb, Microwindows, tinyX) и библиотека виджетов сколько будет весить да и еще чтоб это с пол-тычка портировалось под какую-нибудь архитектуру с ядром 2.4 и процессором от «Дядя Вася Анлимитед». У нас уже наша система портирована под 6 различных платформ, плюс пару эксперементальных. Смею заметить на некоторых платформах используются gcc 2.x.x компиляторы.
Или Вы думаете мы не проводили масштабный ресеч по поводу возможных вариантов? Был рассмотрен вариант даже OpenTV — у них приложение крутится в мультикаст потоке в виде карусели и загружается на клиента по сети по кусочкам (у НТВ на приставках такая штука стоит). Я даже немного покодил на этом кошмаре. Хотя идея просто колосальная.

Я буду очень рад, если предложите вариант лучше.
ЗЫ: Кьют дает нереально большие возможности оптимизации при графической отрисовке в QWS-windows system.
ЗЫЫ: Плюс ко всему мы имеем огромный бонус в виде возможности кодинга и сборки приложения на десктопе и отображение в QVFb — что почти всегда соотвествет действительному результату, за исключением платформо-зависимых багов видео подсистемы на каждом отдельно взятом типе устройств.
Хм, а где вы работаете?
достаточно неприметная небольшая компания SmartLabs (http://smartlabs.tv). Кстати есть один продукт популярный, который мой коллега написал — TsMuxeR называется. Служит для пересборки контейнеров для видео. Тоесть по факту можно из ts файла получить mpeg4 обычный без длительного перекодирования, либо собрать BlueRay диск. Ну вобщем вроде популярная утилитка.
Вообще мы занимаемся IpTv. Сейчас одну из наших поделок можно увидеть на приставках у StreamTv. Вот если у кого есть — это все сделано как раз на Qt. Причем Амина 110 достаточно старая и там стоит PowerPC 405 процессор с 16 Mb постоянной памяти (NORFLASH) и 32 оперативной. Туда мы вместили наше приложение операционку драйверы и кути и еще кучу поделок. Не могу выдавать тайны а на каком этапе у нас внедрение я не знаю.
Хм, очень интересно…
А вам там случайно не нужен студент с непонятным графиком работы, но готовый работать за еду?
=)
боюсь нет :-) компания маленькая, задачи емкие и у нас набирают только ведущих разработчиков. Простых даже не берут а про джуниоров я даже спрашивать боюсь :-)
А, в этом плане — имея qt, без большей части STL и некоторой части буста пожалуй действительно можно обойтись, мысль понял.

Я просто подумал что у вас там STLPort не заводится, а Qt есть — это было бы странно :)
Стл естественно есть. Но он абсолютно не нужен и поддержка его отключена в нашей сборке Qt. лишние 200 кб знаете ли. Мы убрали СТЛ из устройств так как им абсолютно не пользуемся. Не нужен он вообще в нашем случае.
Еще вопросец, а как вы относитесь ко всяким динамическим свойствам(качествам, особенностям, незнаю как лучше перевести) aka property(""); и setProperty(«hello»,true);
Мне вот лично очень понравилась эта фишка, можно без разбухания API увеличивать функционал, без лишней магии манипулировать объектами через яваскрипт и т.д.
Есть плюсы, есть минусы. Как минимум виджеты должны иметь их для того чтобы можно было реализовать плагин для дезайнера и для получения метаинформации из объекта.
Тка что в некоторых случаях это необходимо, но в некоторых избыточно. Как минимум это увеличивает скорость доступа к свойствам.
Если тема интересана — то попробую осветить ее поподробней.
> Как минимум это увеличивает скорость доступа к свойствам.

Видимо имелось в виду время? Нуда, преобразовывать QVariant туда сюда требует некоторого времени, но в некоторых случаях, в основном в GUI, где нету обработки большого объема данных это очень применимо
естественно в гуи в качестве дергания свойства в ответ на реакцию нажатия — это очень гут.
А вот использовать свойства для выполнения сортировке на милионном массиве данных — не очень хорошо :-)
Всему есть свое место применения :-) Для этого думалка и нужна :-)
Ну так в некоторых случаях для обработки больших объемов данных и вовсе можно перейти на Си-style, у нас вот в проге парсилка json'а так и вовсе хоть и использует контейнеры Qt для хранения, но написана в чисто сишном стиле, поэтому работает реактивно, но заглядывать туда без подготовки конечно страшно :) В коде уже нету той стройности и красоты, все принесено в жертву в угоду скорости.
Ну я пару раз даже на асме писал :-) дело то такое :-)
/* Если иерархия классов «широкая» или «глубокая» получается более комплексная структура классов и тем самым повышается удобство «переиспользование — reuse» кода. */

Просветите, что значит «комплексная структура классов»?
Это значит что публичный интерфейс у нас всегда в публичном классе а свойства объекта и логика в приватном. Это можно назвать функциоанльной структуризацией. А так как у нас этих классов целая иерархия то получаем целый комплекс, который построен по общему принципу — Pimpl. Да выражение действительно у меня получилось непонятным. Подумаю как сказать проще.
ммм похоже на сборник рецептов :-)
doc.trolltech.com/qq/qq13-apis.html вот от бывшего троллтека документация официальня :-)
Эмс. Я, конечно, понимаю, что хардкорным Qt'шникам и C++'никам всё понятно, но без примера использования в заключении статьи как-то не очень понятно: а зачем это всё и как это использовать?

И другая непонятка — а при чём тут бинарная совместимость? Как бы это, если вдруг поменяется интерфейс этого самого private-класса, то всё-равно генерируемый ассемблерный код должен поменяться. Ведь, d_func — просто вытаскивает указатель, а потом же в выражениях идёт работа с классом через этот указатель, и через интерфейс, задаваемый типом MyPrivateClass. Вроде, ведь, так. Чего я не понимаю? Объясните, please.
По поводу примера использования?
ну вот давайте на примере Qt рассмотрим.
Ребята разрабатывают библиотку или фреймворк (вернее набор библиотек, в мак ос для этого есть термин — «зонтик», а вот остальные не позаботились :-) ). Буду называть ее как одну библиотеку, пусть даже модульную.
И так они выплевывают релиз 4.0.0 — все дружно начинают присать свои приложения для него и динамически с ним линкуются. Тут выходит библиотека 4.0.1 и баз все приложения которые были собраны с предыдущей версией просто тупо падают, сегментятса или просто отказываются запускатся. В этом случае прийдется пересобирать абсолютно все приложения использующие эту библиотеку, чтобы перелинковать.

Это типо общее описание проблеммы. Qt одни из первых, кто в своих библиотеках обзаботился об ABI. Они гарантируют что все приложения собраные с любой из 4 версией библиотеку будут работать с любой другой библиотекой версии 4. Тоесть я собрал с Qt 4.6 а работать она будет с 4.1 (правда лишь в том случае, если вы не используете новые возможности, которых нет в 4.1. Но даже в этом случае можно разрулить ситуацию програмно, в Qt есть макросы чтобы узнать какой версии библиотеки сейчас в рантайме и либо сообщить юзеру что они сильно старые либо решить ту же задачу но менее оптимальным путем через другую реализацию).

Вот Pimpl для этого и нужен. Что по поводу второй части вопроса. Да конечный машинный код библиотеки изменится, но!!! ABI публичного класса останется тот же. Изменится ABI приватного, но вы же линковались с ABI публичным. Так что все нормально.
Вообще так если кратко, в любой программе (библиотека как один из вариантов) есть два интерфейса — API и ABI. Ну что такое API я думаю объяснять нет необходимости. Это функции с параметрами, переменные и тд. API используется в «Design Time» — при написании кода, а ABI в «Run Time». ABI позволяет ОС правильно запускать приложения и библиотеки и он зависит от ОС (Windows, Linux ...), архитектуры ( I386, PPC, SPARC) и от конкретной реализации (в линукс например есть две популярные реализации загрузчика — Elf и a.out, ABI у них разный). Все сказаное выше очень упрощено, в действительности все немножечко не так :-)

Ну вот начал писать и оказалось что мыслей так много что необходимо выместить их в отдельной статье.
Хм… а разве a.out где-нить, кроме самописных лаб на Си используется? Вообще помоему требование бинарной совместимости это ещё и часть LGPL лицензии
Да на ядре 2.4 встречал системы с монопольной поддержкой a.out. Зачем это так сделано — сам удивлялся, но спросить было не с кого. :-)
Вот по поводу LGPL — не знаю, все может быть. Я честно пытался прочитать LGPL, но всякий раз засыпал. Там все так нечетко написано. Нельзя вот было взять и вынести по пунктам очень кратко. Что-то вроде этого:
1. Можно использовать в комерческом ПО.
2. Можно линковать статически и динамически.
3. Можете не выкладывать в общий доступ при изменении кишков.
4…
ну и так далее. То-есть для простых людей а не для юристов там разных. Для юристов пусть отдельный документ будет. Так как они любят — со всякими уточнениями изъянами и защитой от левопроходства :-)
Вот Pimpl для этого и нужен. Что по поводу второй части вопроса. Да конечный машинный код библиотеки изменится, но!!! ABI публичного класса останется тот же. Изменится ABI приватного, но вы же линковались с ABI публичным. Так что все нормально.

А можно с этого места подробнее? Как в самой программе-то эти два — открытый и закрытый — класса используются? Верно ли я понимаю, что работа со вложенным объектом происходит через d_ptr. Или нет? Вся работа происходит исключительно через класс-обёртку? Но если так, то… Эмс. А чем это лучше чем просто некий стандартный интерфейсный набор функций, и различные реализации их в том же ELF Shared Object? Ну, или dll'ки? Где тут 'фишка'?
Ну упрощенно это так выглядит:
foo.h

class FooPrivate;
class Foo
{
public:
  int somePublic();
  Foo();
private:
   //FooPrivate *m_foo_private; вариант по старинке
   QScopedPointer<FooPrivate> m_foo_private; //но лучше теперь делать так
}

foo.cpp

class FooPrivate
{
  int someMethod ()
  {
     return 1;
  };
}

Foo::Foo() : m_foo_private(new FooPrivate)
{
}

int Foo::somePublicMethod()
{
  return m_foo_private->someMethod();
}


* This source code was highlighted with Source Code Highlighter.

ps
только не забудьте в обьекте соблюсти все необходимые требования от QScopedPointer'а а то иначе не соберется
Да все обращения происходят через d_ptr. Не вся работа происходит через класс-обвертку. Объем этих работ вы можете устанавливать сами, есть три варианта:
1. Все приватные данные храните в приватном классе — вся логика в публичном классе.
2. Все приватные данные храните в приватном классе — та логика, которая перекрывается в дереве наследования выносится в приватный класс а собственная остается в публичном.
3. Вся логика и данные уносятся в приватный класс. — в основном Qt придерживаются этого подхода.
3 метод используется при обычной схеме разработки ПО, когда сначала проектируют модель, интерфейсы в виде схемы (обычно на UML)и потом на основании этого пишется код. Тоесть какие методы должны быть в публичном классе на момент начала написания кода уже известны. А как это реализовано уже прячется в приватный класс. И изменение этой реализации никак снаружи видно не будет.
В итоге с публичным классом можно делать некоторые изменения. Но очень аккуратно. Допустим добавить в конец виртуальный метод. От этого таблица виртуальных методов не поломается, просто в конец добавится метод.

Вот не понял что имеется ввиду под выражением «стандартный интерфейсный набор функций»
Почему не реализовать через dll или so? потому как ломается ABI. Вам всегда нада иметь публичную библиотеку подходящую под ваши приватные библиотеки. В принципе их можно таскать вместе. Но зачем ??? Тут как раз оверхед достаточно большой получается при подходе использовать пару dll. Pimpl очень простой — дописал пару макросов и сделал дополнительный хедер (не обязательно).
Вот не понял что имеется ввиду под выражением «стандартный интерфейсный набор функций»
Почему не реализовать через dll или so? потому как ломается ABI. Вам всегда нада иметь публичную библиотеку подходящую под ваши приватные библиотеки.


Вот я и пытаюсь выяснить, а чем именно ломается ABI-то? ABI же — это calling convention для функций (подпрограмм), методы же, если смотреть в коде, это обычные функции, у которых просто this идёт первым аргументом (ну, условно говоря, на самом деле, это специальный регистр, но не суть). Так вот… Интерфейс спроектирован, public-методы определены, то есть, определён набор функций для взаимодействия с объектом. Ну и всё, что ещё нужно? Если класс будет реализовывать именно такой интерфейс, то ABI никак не должно ломаться. Как бы, в этом вся суть дизайна С++. Иначе, у меня программ *цать работают весьма загадочным образом, потому что некоторые классы реализованы в *.so, которые периодически обновляются, и без всяких pimpl всё линкуется.

Или тут речь идёт о той ситуации, когда нужно часто менять интерфейсы в классе, реализующем функциональность?
this не обязательно идет первым — не забывайте что есть little endian и big endian архитектуры. Но это так к слову. Теперь по сути.
ABI не только calling conversion. Естественно, если интерфейс менятся не будет, а будет менятся только код в cpp файле — то все будет очень хорошо. Имейте ввиду что это не относится к коду, который находится в заголовочном файле. Кстати одна из причин почему не нужно писать его в заголовочном файле, даже если он пустой, потому как надумаете наконец его реализовать, то потеряете эту злощастную бинарную совместимость.
Если вы допустим добавите виртуальный метод в класс и поставите его на первом месте — то виртуальные адреса всех других методов сдвинутся и ваш код, который был слинкован попадет уже в другое место. Если же вы добавите виртуальный метод после всех остальный, то все будет хорошо.
Pimpl — это не панацея и не единственный вариант сохранить бинарную совместимость, я уже приводил ссылку на KDE TechBase где рассказывается что можно делать и останется бинарная совместимость и в каких случаях она поломается. Вот эта статья: techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C++#The_Do.27s_and_Don.27ts

Я планируя посвятить этому тему. Так как это необходимо знать каждому, кто разрабатывает библиотеки для «общего пользования». И расскажу подробней обо всех ньюансах.

Часто встречаются проблеммы с бинарной совместимостью. Как пример — Qt на линукс в бинарниках идет без поддержки Phonon, потому что ни GStreamer ни Xine (которые сейчас поддерживаются как бэкэнды) не заботятся о бинарной совместимости. Поэтому необходимо кути собирать непосредственно с теми библиотеками, которые стоят именно на вашем компьютере. А самое интересно начинается когда вы собираетесь поставлять свою программу со своими кастомными библиотеками Qt на Linux — тут уже наступает «ахтунг», либо тянуть GStreamer с собой или Xine. Либо в качестве зависимостей тянуть девелоперские пакеты для одной из этих библиотек и собирать фонон уже в скрипте при установке либо требовать чтобы пользователь об этом позаботился сам ну или использовать системный фонон и прописать его в зависимости. Все варианты не очень. И все из-за того что кто-то не позаботился о бинарной совместимости. Хотя я слышал что и Xine и GStreaner пообещали впредь относится серьезней и далее ситуация должна изменится.
Проблемма бинарной совместимости есть и ее просто необходимо знать, но увы очень мало документации по этой теме. Так что я собираюсь пятую статью посвятить именно этому вопросу.
Угу. Понятно. Thnx за разъяснение. Только эта :) big и little endian — это к порядку битов в машинном слове.
Вот кстати когда поставил Qt 4.6 то радостно наступил на проблему бинарной совместимости с Xine'ом. Пришлось старую версию бэкенда ставить, ибо пересборкой небыло никакого желания заниматься
Хорошая статья, однако:
1) фраза «Одним из самых основных достоинств — это сохранение бинарной совместимости» не согласована, подправьте плиз.
2) там ещё скобочки впритык к словам встречаются

3) Хорошо бы упомянуть для понятности, что всякие беседы про скорость компиляции за счёт сокращения объёма включаемых библиотек — это касается С-образного подхода к модульности с их вечными инклюдами всяких там *.h-файлов, которые не требуют инклюдов реализации. А тех, кто пишет на других языках — типа наследников Паскаля, например, или php — в котором линкуемых библиотек нету, и всё что есть, инклюдится целиком, а не заголовками — этот бонус ну совершенно не касается. То есть, следование такому паттерну им не так полезно, и патерн получается более ограниченной области применения, чем у Вас описано. Вообще не помешало бы поделить в части описания бонусов подхода, какие из них проявляются только в С, а какие действительно универсальны.
Спасибо за критику. Первые два пункта абсолютно согласен.

Вот третий — блог посвящен Qt, речь идет об внутренностях и подходах Qt и речь идет именно о чисто Qt-style подходу реализации этого паттерна. Речь идет о паттерне Pimpl d-pointer. Ввиду вышеуказанных обстоятельств можно предположить что это все касается исключительно C++ или гибридных языков, или языков которые в себя могут включать C++ (например тот же самый Objective-C).

По поводу php — я даже не знаю зачем этот паттерн там вообще нужен :-). Может потому-что я в php не силен, а может потому-что он там действительно не нужен. Интерпретатор и ABI вещи не совместимые :-)
Ну я думаю, чтобы можно было разнести фронтенды и бэкенды, типа публичный класс — фронтенд, а приватный — бэкенд, но мне кажется в контексте php это конь пятиногий
ну вот и я о том. Pimpl по версии Qt интересен там где интерфейс и реализация вынесены в отдельные файлы. Ну кстати вот в Objective-C — это тоже не очень то нужно. Ибо там все по другому и таких проблемм не возникнет :-) берешь и спрашиваешь у класса:«а скажи ка мне тварищь, а есть ли у тебя метод „а“? Если есть то дерни ка мне его с вот таким вот списком параметров.Если нету то добавь ка его с вот такой реализацией и дерни его с вот таким вот списком параметров :-) ».
Qt портирован под многие языки, и пара слов про сишную направленность статьи и описываемых бонусов лично меня бы избавила от целого ряда недоумений.
Ну вообще то Qt не был портирован ни на один из языков. Он существует, существовал и будет существовать только на C++. Что касается QtJambi, PyQt, QT-D, Qt# и им подобных — это биндинг — тоесть привязка. Само же Qt на С++. В большей степени эти биндинги используются как инструменты для этих хост-языков для реализации GUI (отсюда и миф, что Qt — это GUI Framework).
Но для ясности допишу в начале.
Поправьте пример с форвардом, а то у вас там идет форвард привата, а на следующей строке объявление привата (должен быть паблик).

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

Немного занудства:
Смысл в том что объявляется класс XXXPrivate и переменная публичного класса в защищенной секции. В отдельном заголовочном файле или в .cpp файле уже пишется объявление приватного класса

Во втором предложении точнее будет сказать «определение» (definition против declaration).

А вообще, интересная, похоже, штука — эта Qt. Надо будет обязательно тоже попробовать.
Не так этот reinterpret_cast и страшен, как кажется, его можно абсолютно спокойно использовать, например, если указатель просто является ключем в каком нить хэш массиве, тогда к указателю нет нужды обращаться и абсолютно всеравно, на что он указывает, а вот компилятору в общем то нет, тут как раз reinterpret_cast и спасает, я через него логику убирания удалённых элементов из хэш массива делал. Объект при удалении выбрасывает сигнал, в котором есть указатель на область памяти, где он находился, а потом уже можно его из массива смело удалить
никто не говорит что reinterpret_cast так страшен, просто нада внятно думать прежде чем им пользоватся. Вот вроде у Страуструпа читал что такое длинное название вместо сишых простых скобок именно для того чтобы человек четко понимал что он делает. Если бы reinterpret_cast был явным злом его бы и небыло бы :-)
А он не совсем сишный каст, он еще константность контролирует хоть как то. Но его нужно делать в тех случаях, когда точно уверен, что перед тобой за объект, а то можно получить очень интересные эффекты и падение будет далеко не самым скучным из последствий
Может просто ночь уже, но вроде лучше так:
Это необходимо для того чтобы обеспечить возможность создания наследников приватного класса и использования их как приватных классов во всей иерархии

И еще, почему бы не отдать инициализацию q_ptr приватному классу:
MyClass::MyClass(QObject* parent)
    :QObject(parent)
    ,d_ptr(NULL)
{ 
    d_ptr = new MyClassPrivate(this);
    .......
}
С соответствующим конструктором MyClassPrivate.
Раз уж на то пошло, можно делать сразу:

MyClass::MyClass(QObject* parent)
  : QObject(parent), d_ptr(new MyClassPrivate(this))
{ ... }

...

MyClassPrivate::MyClassPrivate(MyClass* parent)
  : q_ptr(parent)
{ ... }
ну как по мне, так то что написал std, и Вы cyberbobs, не очень удобно, так как требует дополнительного параметра в конструктор приватного объекта.

По производительности никаких бонусов тут нет. Но строки
Q_D(MyClass);
d->q_ptr = this;


будут присутствовать только в конструкторе самого базового объекта, в остальных же ничего подобного фигурировать не будет. А параметр конструктора приватного класса придется передавать во всей иерархии.
Поэтому в моем подходе конструктор наследника будет выглядеть таким образом:
MyClassDerived::MyClassDerived(QObject *parent)
:MyClass(*new MyClassDerivedPrivate(), parent)


А в вашем:
MyClassDerived::MyClassDerived(QObject *parent)
:MyClass(*new MyClassDerivedPrivate(this), parent)


поэтому достаточно спорно тут как лучше, мне кажется в моем варианте лучше, так как необязательно создавать конструктор, пусть будет конструктор по умолчанию, если необходимо просто проинициализировать q_ptr (и то если он нужен). А если он нужен, то это действие делается только в конструкторе одного объекта а не тянется вся эта эпопея с инициализацией в них. как минимум снижается риск, что в конструктор подчиненного объекта будет помещен не тот объект. То есть в моем случае код более «ошибкостойкий», опять же ИМХО. Хорошо, что С++ дают простор для фантазии :-)
По большей части, у меня в коде до сокрытия деталей реализации доходит тогда, когда в Private-классе нужно в любом случае делать инициализацию ряда его полей. В конечном итоге — это сугубое дело вкуса :)
бонус есть: явно указываем в приватном классе, что ему нужен «родитель» и указываем ему «родителя» сразу при создании
ну как раз это нужно только самому первому приватному классу, все остальные унаследуют у него. Получается позаботиться об этом нужно всего один раз, и особенно это актуально, если наследников будет писать кто-нибудь другой (например индус), и будет жаловаться что у него там не работает, а он допустим в приватный класс передал (0), так как было лень читать документацию что туда нужно ложить this. Так как нам в любом случае нужно в приватный класс положить именно этот this то не вижу необходимости это делать везде в иерархии, лучше сделать один раз в вершине дерева и забыть про это.
… и получить варнинг за использование this в списке инициализации. Не фатально конечно, но неприятно
не понял где мы тут получаем ворнинг? и за что?
А ну это естественно если идти не по мной предложенному пути. В моем случае опять же такой штуки не будет (вернее Qt-style way).
Проверю это в GCC. Вроде он молчит в данном случае даже при -Wall
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории