Требования
- Я пишу класс под названием
RCObject
, который означает "Reference Counted Object"; - Класс
RCObject
должен быть абстрактным, служащим базовым классом фреймворка (EС++ 3 Пункт 7); -
Создание экземпляров подклассов
RCObject
в стеке должно быть запрещено (MEС++ 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 };
-
CLARIFIED: Объявление/возврат необработанных указателей на
RCObject
(и подклассы) должно быть запрещено (к сожалению, я не думаю, что существует практический способ его принудительного применения, т.е. запуск ошибки компиляции, когда пользователи не следуют). См. Пример исходного кода в пункте 3 выше; - Экземпляры подклассов
RCObject
должны быть клонированы так же, какCloneable
в Java. (MEС++ 1 Пункт 25); -
Подклассы пользователей
RCObject
должны иметь возможность записывать "Factory Методы" для своих подклассов. Не должно быть утечки памяти, даже если возвращаемое значение игнорируется (не назначается переменной). Механизм, близкий к этому,autorelease
в Objective-C;[ ADDED: cschwan и Кос указал, что возвращение "smart-pointer-to-
RCObject
" достаточно для выполнения требования. ] -
CLARIFIED: Экземпляры подклассов
RCObject
должны содержаться в соответствующем контейнереstd::
илиboost::
. Мне в основном нужен контейнерstd::vector
-like, контейнерstd::set
-like и контейнерstd::map
-like. Базой является то, чтоintrusive_ptr<RCObject> my_bear = v[10];
и
m["John"] = my_bear;
работает как ожидалось;
- Исходный код должен быть скомпилирован с использованием компилятора С++ 98 с ограниченной поддержкой С++ 11 (точнее, Visual Studio 2008 и gcc 4.6).
Дополнительная информация
- Я новичок в Boost (Boost настолько велик, что мне нужно некоторое время, чтобы быть знакомым с ним. существующие готовые решения, но у него есть высокая вероятность, что я не знаю такого решения);
- В связи с соображениями производительности я хотел бы использовать
intrusive_ptr
вместоshared_ptr
, но я открыт для них обоих и даже любых других предложений; - Я не знаю,
make_shared()
,allocate_shared()
,enable_shared_from_this
() может помочь (кстати,enable_shared_from_this
(), по-видимому, не очень продвинут в Boost - его даже не найти в умный указатель главная страница); - Я слышал о "написании пользовательских распределителей", но я боюсь, что это слишком сложно;
- Интересно, следует ли
RCObject
унаследовать отboost::noncopyable
конфиденциально; - Я не смог найти существующую реализацию, которая удовлетворяет всем моим требованиям;
- Рамка - это игровой движок;
- Основными целевыми платформами являются Android и iOS;
- Я знаю
intrusive_ptr_add_ref()
иintrusive_ptr_release()
и как их реализовать, используя Аргумент-зависимый поиск (например, 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
}
"Неинтеллектуальная" версия - createAnimal() [Factory Метод]
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;
}
"Неинтеллектуальная" версия - main()
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 (принятый ответ выглядит элегантно!!! какой-то "пользовательский deallocator"? Я не уверен, как он сравнивается с
intrusive_ptr
с точки зрения эффективности времени и пространства) -
intrusive_ptr в С++ 11 (принятый ответупомянутый
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 (принятый ответ отметил, что пока
std::enable_shared_from_this()
должно быть хорошо,boost::enable_shared_from_this()
, похоже, имеет некоторые проблемы)
Ссылки
- [EС++ 3]: Эффективный С++: 55 конкретных способов улучшить ваши программы и разработки (3-е издание) Скотта Мейера
- Пункт 7: объявить деструкторы виртуальными в полиморфных базовых классах
- [MEС++ 1]: более эффективный С++: 35 новых способов улучшить ваши программы и проекты (1-е издание) Скотта Майера
- Пункт 25: Виртуализация конструкторов и функции, не являющиеся членами.
- Пункт 27: Требование или запрещение объектов на основе кучи.