Что лучший способ состоит в том, чтобы реализовать интеллектуальные указатели в C++?

Я оценивал различные реализации интеллектуального указателя (ничего себе, существует МНОГО там), и мне кажется, что большинство из них может быть категоризировано в две широких классификации:

1) Эта категория использует наследование на объектах, на которые ссылаются так, чтобы у них были подсчеты ссылок и обычно () и вниз () (или их эквиваленты) реализованный. IE, для использования интеллектуального указателя, объекты, на которые Вы указываете, должен наследоваться немного, классифицируют касательно реализации, обеспечивает.

2) Эта категория использует вторичный объект содержать подсчеты ссылок. Например, вместо того, чтобы указать на интеллектуальный указатель прямо на объект, это на самом деле указывает на этот объект метаданных... У кого есть подсчет ссылок и () и вниз () реализации (и кто обычно обеспечивает механизм для указателя для достигания указываемого фактического объекта, так, чтобы интеллектуальный указатель мог правильно реализовать оператор-> ()).

Теперь, 1 имеет оборотную сторону, что она вызывает все объекты, которые требуется к подсчету ссылок наследовать от общего предка, и это означает, что Вы не можете использовать, это к подсчету ссылок возражает, что Вы не управляете исходным кодом к.

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

Теперь, насколько я понимаю, повышение:: shared_pointer использует механизм 2, или что-то как он... Тем не менее я не могу вполне составить свой ум, который хуже! Я только когда-либо использовал механизм 1 в производственном коде... У кого-либо есть опыт с обоими стилями? Или возможно существует иначе, это лучше, чем оба из них?

12
задан Deduplicator 16 August 2019 в 17:44
поделиться

9 ответов

"Что лучший способ состоит в том, чтобы реализовать интеллектуальные указатели в C++"

  1. Не делайте! Используйте существующий, хорошо протестированный интеллектуальный указатель, такой как повышение:: shared_ptr или станд.:: tr1:: shared_ptr (станд.:: unique_ptr и станд.:: shared_ptr с C++ 11)
  2. Если Вы имеете к, то не забудьте:
    1. используйте безопасную-bool идиому
    2. обеспечьте оператор->
    3. обеспечьте сильную гарантию исключения
    4. зарегистрируйте требования исключения, которые Ваш класс делает на средстве удаления
    5. используйте copy-modify-swap, если это возможно, для реализации сильной гарантии исключения
    6. документ, обрабатываете ли Вы многопоточность правильно
    7. запишите обширные модульные тесты
    8. преобразование в основу реализации таким способом, которым это удалит на полученном типе указателя (policied интеллектуальные указатели / динамические интеллектуальные указатели средства удаления)
    9. доступ получения поддержки к необработанному указателю
    10. полагайте, что cost/benifit обеспечения слабых указателей повреждает циклы
    11. обеспечьте соответствующие операторы кастинга для своих интеллектуальных указателей
    12. сделайте своего конструктора шаблонным для обработки указателя базы построения от полученного.

И не забывайте ничего, что я, возможно, забыл в вышеупомянутом неполном списке.

26
ответ дан 2 December 2019 в 03:04
поделиться

Только для предоставления другого представления к повсеместному ответу Повышения (даже при том, что это - правильный ответ для многого использования) смотрите на реализацию Loki интеллектуальных указателей. Для дискурса на принципах проектирования исходный создатель Loki записал книге современный Дизайн C++.

9
ответ дан 2 December 2019 в 03:04
поделиться

Я использовал повышение:: shared_ptr в течение нескольких лет теперь и в то время как Вы правы относительно оборотной стороны (никакое присвоение через возможный указатель), я думаю, что это определенно стоило того из-за огромного количества связанных с указателем ошибок, от которых это сохранило меня.

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

7
ответ дан 2 December 2019 в 03:04
поделиться

Это кажется мне, этот вопрос отчасти похож на выяснение, "Которое является лучшим алгоритмом сортировки?" Нет никакого ответа, он зависит от Ваших обстоятельств.

В моих собственных целях я использую Ваш тип 1. У меня нет доступа к библиотеке TR1. Я действительно имею полный контроль над всеми классами, на которые я должен совместно использовать указатели. Дополнительная память и эффективность времени типа 1 могли бы быть довольно небольшим, но использованием памяти, и скорость большие проблемы для моего кода, таким образом, тип 1 был хлопком Данк.

С другой стороны, для любого, кто может использовать TR1, я думал бы станд. типа 2:: tr1:: класс shared_ptr был бы разумным выбором по умолчанию, чтобы использоваться каждый раз, когда нет некоторой нажимающей причины не использовать его.

2
ответ дан 2 December 2019 в 03:04
поделиться

Если Вы будете придерживаться тех, которые находятся в стандартной библиотеке, то Вы будете в порядке.
Хотя существует несколько других типов, чем те, Вы указали.

  • Совместно использованный: Где владение совместно используется несколькими объектами
  • Принадлежавший: Где один объект владеет объектом, но передача позволяется.
  • Неперемещаемый: Где один объект владеет объектом, и он не может быть передан.

Стандартная библиотека имеет:

  • станд.:: auto_ptr

Повышение имеет пару больше, чем было адаптировано tr1 (следующая версия стандарта)

  • станд.:: tr1:: shared_ptr
  • станд.:: tr1:: weak_ptr

И те, которые все еще в повышении (который в относительно необходимая вещь так или иначе), которые, надо надеяться, превращают его в tr2.

  • повышение:: scoped_ptr
  • повышение:: scoped_array
  • повышение:: shared_array
  • повышение:: intrusive_ptr

См.: Интеллектуальные указатели: Или кто владеет Вами ребенок?

3
ответ дан 2 December 2019 в 03:04
поделиться

Повышение также имеет навязчивый указатель (как решение 1), который не требует наследования ни от чего. Это действительно требует, чтобы изменение указателя на класс сохранило подсчет ссылок и обеспечило соответствующие функции членства. Я использовал это в случаях, где эффективность памяти была важна, и не хотела издержки другого объекта для каждого общего используемого указателя.

Пример:

class Event {
public:
typedef boost::intrusive_ptr<Event> Ptr;
void addRef();
unsigned release();
\\ ...
private:
unsigned fRefCount;
};

inline void Event::addRef()
{
  fRefCount++;
}
inline unsigned Event::release(){
    fRefCount--;
    return fRefCount;
}

inline void intrusive_ptr_add_ref(Event* e)
{
  e->addRef();
}

inline void intrusive_ptr_release(Event* e)
{
  if (e->release() == 0)
  delete e;
}

Определение типа Ptr используется так, чтобы я мог легко switcth между повышением:: shared_ptr <> и повышение:: intrusive_ptr <>, не изменяя клиентского кода

3
ответ дан 2 December 2019 в 03:04
поделиться

Проблема с 2 может работаться вокруг. Повышение предлагает повышение:: shared_from_this по этой той же причине. На практике это не большая проблема.

Но причина, они пошли с Вашей опцией № 2, состоит в том, что она может использоваться во всех случаях. Доверие наследованию является не всегда опцией, и затем Вас оставляют с интеллектуальным указателем, который Вы не можете использовать для половины Вашего кода.

Я должен был бы сказать, что № 2 является лучшим, просто потому что он может использоваться при любых обстоятельствах.

1
ответ дан 2 December 2019 в 03:04
поделиться

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

В целом различия между этими двумя типами указателей не были существенными. Так как единственное исключение - это рано версии нашего ненавязчивого указателя, неявно преобразованного из необработанного указателя, и это может легко привести к проблемам памяти, если указатели используются неправильно:

void doSomething (NIPtr<int> const &);

void foo () {
  NIPtr<int> i = new int;
  int & j = *i;
  doSomething (&j);          // Ooops - owned by two pointers! :(
}

Только что некоторый рефакторинг привел к некоторым частям кода, объединяемого, и таким образом, выбор должен был быть сделан о который тип указателя использовать. Ненавязчивому указателю теперь объявили конструктора преобразования как явного и таким образом, было решено пойти с навязчивым указателем, чтобы экономить на изменении объема кода, которое требовалось.

К нашему большому удивлению одна вещь, которую мы действительно замечали, состояла в том, что у нас было непосредственное повышение производительности при помощи навязчивого указателя. Мы не поместили много исследования этого и просто предположили, что различием была стоимость поддержания объекта количества. Возможно, что другие реализации ненавязчивого общего указателя решили эту проблему к настоящему времени.

1
ответ дан 2 December 2019 в 03:04
поделиться

О чем Вы говорите, навязчивые и ненавязчивые интеллектуальные указатели. Повышение имеет обоих. boost::intrusive_ptr вызывает функцию, чтобы уменьшить и увеличить подсчет ссылок Вашего объекта, каждый раз это должно изменить подсчет ссылок. Это не называет функции членства, но бесплатные функции. Таким образом, это позволяет управлять объектами без потребности изменить определение их типов. И как Вы говорите, boost::shared_ptr ненавязчиво, Ваша категория 2.

У меня есть ответ, объясняя intrusive_ptr: Создание shared_ptr не использует, удаляют. Короче говоря, Вы используете его, если у Вас есть объект, который уже имеет подсчет ссылок или потребность (как Вы объясняете), объект, на который уже ссылаются для владения intrusive_ptr.

1
ответ дан 2 December 2019 в 03:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: