pimpl: избежать указателя на указатель с помощью pimpl - PullRequest
3 голосов
/ 07 апреля 2011

В этом вопросе я спросил "pimpl: shared_ptr или unique_ptr" Я был убежден, что правильное использование идиомы pimpl - это использование unique_ptr, а не shared_ptr.Он должен действовать для пользователя так, как будто указатель вообще отсутствует, тогда как shared_ptr вводит псевдоним при копировании, который определенно действует как указатель.

Итак, допустим, пользователь хочет создатьshared_ptr моему объекту pimpl (скажем, если они действительно хотят, чтобы ему было несколько псевдонимов).Например:

shared_ptr<my_pimpl> p(new my_pimpl());

Это приведет к shared_ptr, указывающему на unique_ptr, указывающему на мою реализацию.

Было бы неплохо, если бы я мог добиться чего-то вроде следующего:

my_pimpl x; // (1)
shared_ptr<my_pimpl> p(new my_pimpl()); // (2) Pointer to pointer here.
x.f(); // (3)
p->f(); // (4)

, но каким-то образом избавиться от указателя на указатель, сохранив при этом скрытие реализации pimpl.

Есть идеи, как этого добиться (я рад изменить строку (2) и, очевидно, my_pimpl, но хочу, чтобы строки (3) и (4) остались прежними). ​​

Ответы [ 2 ]

2 голосов
/ 07 апреля 2011

Существует несколько возможных подходов в зависимости от ваших ограничений.

1. Создайте свой собственный класс shared_my_pimpl

Создайте класс shared_my_pimpl, который имеет тот же интерфейс, что и my_pimpl, но внутренне использует shared_ptr вместо unique_ptr. Теперь создайте класс shared_ptr_my_pimpl, который содержит shared_my_pimpl и имеет operator->, который возвращает указатель на shared_my_pimpl, так что вы получаете обозначение -> вместо . для доступа к элементу. Вы можете добавить функцию make_shared_ptr_my_pimpl, чтобы она выглядела как использование shared_ptr.

Недостатки:

  1. Тип объекта не shared_ptr<x>, а shared_ptr_my_pimpl; это просто притворяется shared_ptr.
  2. Вы не можете получить my_pimpl* или my_pimpl& к объекту; это другой тип, который ведет себя одинаково.

2. Производная от интерфейса

Создайте интерфейс my_pimpl_interface со всеми соответствующими функциями чисто виртуальными. Из этого интерфейса извлеките как my_pimpl, так и my_pimpl::impl (ваш класс реализации pimpl). Добавьте функцию make_shared_my_pimpl, которая возвращает shared_ptr<my_pimpl_interface> в my_pimpl::impl. Теперь вы можете ссылаться как на простой объект, так и на объект shared_ptr как my_pimpl_interface&.

Недостатки:

  1. Делая все функции виртуальными, вы вызываете дополнительную косвенность при их вызове, чего, возможно, и пытались избежать. Ваш стандартный my_pimpl объект также оплатит эти накладные расходы.
0 голосов
/ 07 апреля 2011

Вы должны использовать интерфейс для этой цели, потому что тогда пользователи вашего класса могут выбирать, хотят ли они использовать shared_ptr или unique_ptr.

...