RCObject
, что расшифровывается как «объект подсчета ссылок»;RCObject
должен быть абстрактным, служащим базовым классом фреймворка (EC ++ 3 Пункт 7);Создание экземпляров RCObject
подклассы в стеке должны быть запрещены (MEC ++ 1 Пункт 27);
Bear
конкретный подкласс RCObject
]
[ C.E.
здесь означает Ошибка компиляции]
Bear b1; // Triggers C.E. (by using MEC++1 Item 27)
Bear* b2; // Not allowed but no way to trigger C.E.
intrusive_ptr<Bear> b3; // Recommended
Bear* bs1 = new Bear[8]; // Triggers C.E.
container< intrusive_ptr<RCObject> > bs2; // Recommended
intrusive_ptr_container<RCObject> bs3; // Recommended
class SomeClass {
private:
Bear m_b1; // Triggers C.E.
Bear* m_b2; // Not allowed but no way to trigger C.E.
intrusive_ptr<Bear> m_b3; // Recommended
};
ПРОЯСНЕНЫ: Объявление / возврат необработанных указателей на RCObject
(и подклассы) должны быть запрещены (К сожалению, я не думаю, что существует практический способ обеспечить его выполнение, то есть вызвать ошибки компиляции, когда пользователи не следуют). См. Пример исходного кода в пункте 3 выше;
RCObject
подклассы должны быть клонируемыми так же, как Cloneable
на Яве. (MEC ++ 1 Пункт 25);Подклассы пользователей RCObject
должен уметь писать «Фабрика Методы» для своих подклассов. Не должно быть утечки памяти, даже если возвращаемое значение игнорируется (не присваивается переменной). Механизм, который близок к этому autorelease
в Objective-C;
RCObject
«достаточно для выполнения требования.]
ПРОЯСНЕНЫ: Экземпляры RCObject
Подклассы должны иметь возможность содержаться в соответствующем std::
или же boost::
контейнер. Мне в основном нужнаstd::vector
«контейнер»std::set
«контейнер и»std::map
как контейнер. Базовая линия
intrusive_ptr<RCObject> my_bear = v[10];
а также
m["John"] = my_bear;
работать как положено;
intrusive_ptr
вместо shared_ptr
, но я открыт для них обоих и даже для любых других предложений;make_shared()
, allocate_shared()
, enable_shared_from_this
() может помочь (кстати, enable_shared_from_this
() кажется, не очень популярен в Boost — его даже нельзя найти в умный указатель Главная страница);RCObject
должно быть унаследовано от boost::noncopyable
в частном порядке;intrusive_ptr_add_ref()
а также intrusive_ptr_release()
и как их реализовать используя Зависимый от аргумента поиск (aka. Koenig Lookup);boost::atomic_size_t
с boost:intrusive_ptr
,namespace zoo {
class RCObject { ... }; // Abstract
class Animal : public RCObject { ... }; // Abstract
class Bear : public Animal { ... }; // Concrete
class Panda : public Bear { ... }; // Concrete
}
zoo::Animal* createAnimal(bool isFacingExtinction, bool isBlackAndWhite) {
// I wish I could call result->autorelease() at the end...
zoo::Animal* result;
if (isFacingExtinction) {
if (isBlackAndWhite) {
result = new Panda;
} else {
result = new Bear;
}
} else {
result = 0;
}
return result;
}
int main() {
// Part 1 - Construction
zoo::RCObject* object1 = new zoo::Bear;
zoo::RCObject* object2 = new zoo::Panda;
zoo::Animal* animal1 = new zoo::Bear;
zoo::Animal* animal2 = new zoo::Panda;
zoo::Bear* bear1 = new zoo::Bear;
zoo::Bear* bear2 = new zoo::Panda;
//zoo::Panda* panda1 = new zoo::Bear; // Should fail
zoo::Panda* panda2 = new zoo::Panda;
// Creating instances of RCObject on the stack should fail by following
// the method described in the book MEC++1 Item 27.
//
//zoo::Bear b; // Should fail
//zoo::Panda p; // Should fail
// Part 2 - Object Assignment
*object1 = *animal1;
*object1 = *bear1;
*object1 = *bear2;
//*bear1 = *animal1; // Should fail
// Part 3 - Cloning
object1 = object2->clone();
object1 = animal1->clone();
object1 = animal2->clone();
//bear1 = animal1->clone(); // Should fail
return 0;
}
/* TODO: How to write the Factory Method? What should be returned? */
#include <boost/intrusive_ptr.hpp>
int main() {
// Part 1 - Construction
boost::intrusive_ptr<zoo::RCObject> object1(new zoo::Bear);
boost::intrusive_ptr<zoo::RCObject> object2(new zoo::Panda);
/* ... Skip (similar statements) ... */
//boost::intrusive_ptr<zoo::Panda> panda1(new zoo::Bear); // Should fail
boost::intrusive_ptr<zoo::Panda> panda2(new zoo::Panda);
// Creating instances of RCObject on the stack should fail by following
// the method described in the book MEC++1 Item 27. Unfortunately, there
// doesn't exist a way to ban the user from declaring a raw pointer to
// RCObject (and subclasses), all it relies is self discipline...
//
//zoo::Bear b; // Should fail
//zoo::Panda p; // Should fail
//zoo::Bear* pb; // No way to ban this
//zoo::Panda* pp; // No way to ban this
// Part 2 - Object Assignment
/* ... Skip (exactly the same as "non-smart") ... */
// Part 3 - Cloning
/* TODO: How to write this? */
return 0;
}
Приведенный выше код («Умная версия») показывает ожидаемую схему использования. Я не уверен, соответствует ли этот шаблон использования передовым методам использования интеллектуальных указателей или нет. Пожалуйста, поправьте меня, если это не так.
сделать shared_ptr не использовать delete (The принятый ответ выглядит элегантно !!! Это что-то вроде «нестандартного освобождения»? Я не уверен, как это сравнивается с intrusive_ptr
с точки зрения времени и эффективности пространства)
intrusive_ptr в C ++ 11 (The принятый ответ упомянутый make_shared()
а также enable_shared_from_this
(), но я не понимаю часть «но это не позволяет вам управлять типом, используя разные типы интеллектуальных указателей»)
Есть ли способ повысить эффективность shared_ptr путем хранения счетчика ссылок внутри контролируемого объекта? (Все еще переваривает)
intrusive_ptr: Почему не предоставляется общий базовый класс? (Ответы недостаточно подробны)
Это допустимое использование intrusive_ptr? (Из этого я кое-что узнал, но этот вопрос был сосредоточен на «передаче необработанного указателя в функцию, которая принимает умный указатель»)
Подсчет ссылок с помощью универсального навязчивого указателя клиента (Этот использовал «CRTP», но я боюсь, что дальнейшие подклассы сделают мне головную боль — я должен сделать zoo::Panda
простираясь от zoo::Bear
в одиночку, или я должен сделать это расширяя оба zoo::Bear
а также intrusive_base<zoo::Panda>
?)
Встроенный счетчик ссылок с Boost shared_ptr (The принятый ответ упомянул, что в то время как std::enable_shared_from_this()
должно быть в порядке, boost::enable_shared_from_this()
Кажется, есть некоторые проблемы)
make_shared
создает экземпляр вашего класса в том же блоке выделения, что и счетчик ссылок. Я не уверен, почему вы думаете intrusive_ptr
будет иметь лучшую производительность: это замечательно, когда уже есть механизм подсчета ссылок, который вы не можете удалить, но здесь это не так.
Для клона я бы реализовал его как бесплатную функцию, которая берет умный pojnter и возвращает то же самое. Он является другом и вызывает частный чисто виртуальный метод клонирования в базе, который возвращает общий указатель на базу, а затем выполняет быстрый интеллектуальный указатель на общий указатель на производный. Если вы предпочитаете клон в качестве метода, используйте crtp, чтобы дублировать это (давая частному клону имя типа secret_clone
). Это дает ковариантные возвращаемые типы интеллектуальных указателей с небольшими накладными расходами.
Crtp с рядом базовых классов часто позволяет передавать как базовые, так и производные классы. Класс crtp наследуется от base и имеет обычный self()
что возвращает производное.
Фабричные функции должны возвращать умный указатель. Вы можете использовать специальный трюк для удаления, чтобы получить метидный вызов перед уничтожением для последней очистки.
Если вы полностью параноик, вы можете заблокировать большинство способов получить необработанный указатель или ссылку на ваш класс: оператор блока * на интеллектуальном указателе. Тогда единственный путь к необработанному классу — это явный вызов operator->
,
Другой подход к рассмотрению unique_ptr
и ссылки на одно и то же. Вам нужно совместное владение и управление временем жизни? Это делает некоторые проблемы проще (совместное владение).
Обратите внимание, что висячие слабые указатели не позволяют перераспределять память.
Серьезным недостатком использования интеллектуальных указателей является то, что вы не можете иметь экземпляры стека или экземпляры непосредственно внутри контейнеров. Оба из них могут быть серьезным увеличением производительности.
- Создание экземпляров
RCObject
подклассы в стеке должны быть запрещены ([MEC ++ 1] [mec ++ 1] Item 27);
Каково ваше обоснование? MEC ++ приводит пример «объектов, способных совершить самоубийство», что может иметь смысл в контексте игровой среды. Это тот случай?
Это должно быть возможно сделать с достаточно умным указателем, если вы настаиваете на том, чтобы избежать более простого обходного пути.
Обратите внимание, что в этом случае вы, возможно, захотите также запретить создание массивов таких объектов в стеке, используя new[]
— это также предотвращает удаление одного. Вы, вероятно, также захотите запретить использование RCObjects в качестве подобъектов (членов других классов). Это будет означать, что вы вообще не разрешаете использовать значения RCObject и позволяете клиентскому коду обрабатывать их только с помощью умных указателей.
- Объявление / возврат необработанных указателей на
RCObject
(и подклассы) следует избегать (к сожалению, я не думаю, что существует способ обеспечить его выполнение, выдавая ошибки компиляции);
Тогда у вас обязательно будут слабые указатели, чтобы можно было сказать: «Я заинтересован в этом объекте, но я не поддерживаю его».
- Подклассы пользователей
RCObject
должен иметь возможность написать [«Factory Methods»] [factory_method] для своих подклассов. Не должно быть утечки памяти, даже если возвращаемое значение игнорируется (не присваивается переменной).
Такая функция будет возвращать временный объект умного указателя со счетчиком ссылок, равным 1. Если этот временный объект не используется для инициализации другого (таким образом, дополнительно увеличивая счетчик ссылок), он очистит объект. Ты в безопасности.
- Экземпляры
RCObject
Подклассы должны быть в состоянии содержаться вstd::
или жеboost::
контейнер (или что-то подходящее). Мне в основном нужно что-то похожее наstd::vector
,std::set
а такжеstd::map
;
Этот вид не согласуется с (3). Если вы настаиваете на том, чтобы объекты создавались индивидуально в куче и передавались через интеллектуальные указатели (не как значения), то вам также следует использовать контейнеры интеллектуальных указателей.
- Из соображений производительности я хотел бы использовать [
intrusive_ptr
] [intrusive_ptr] вместо [shared_ptr
] [shared_ptr], но я открыт для них обоих и даже для любых других предложений;
Разве вы не оптимизируете преждевременно?
Кроме того, я считаю, что использование навязчивых указателей устраняет возможность использования слабых ссылок — которые вам, скорее всего, понадобятся, как я уже упоминал ранее.
- Интересно,
RCObject
должно быть унаследовано от [boost::noncopyable
] [noncopyable] в частном порядке;
Если вы запрещаете переменные типа значения и предоставляете виртуальный клон, то, вероятно, нет необходимости в общедоступном конструкторе копирования. Вы можете сделать частную копию ctor и использовать ее при определении Clone.