要求
- 我正在编写一个名为 的类
RCObject
,它代表“引用计数对象”; - 该类
RCObject
应该是抽象的,作为框架的基类(EC++3 Item 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
在 Java 中一样。(MEC++1第 25 条); 子类化的用户
RCObject
应该能够为他们的子类编写“工厂方法”。即使返回值被忽略(未分配给变量),也不应该有内存泄漏。与此接近的机制是autorelease
在 Objective-C 中。澄清:子类的实例
RCObject
应该能够包含在适当的std::
容器boost::
中。我主要需要一个“std::vector
-like”容器,一个“std::set
-like”容器和一个“std::map
-like”容器。基线是intrusive_ptr<RCObject> my_bear = v[10];
和
m["John"] = my_bear;
按预期工作;
- 源代码应该可以使用具有有限 C++11 支持的 C++98 编译器进行编译(确切地说是 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()
如何使用Argument-Dependent Lookup (aka. Koenig Lookup)来实现它们; - 我知道如何使用
boost::atomic_size_t
withboost:intrusive_ptr
。
类定义
namespace zoo {
class RCObject { ... }; // Abstract
class Animal : public RCObject { ... }; // Abstract
class Bear : public Animal { ... }; // Concrete
class Panda : public Bear { ... }; // Concrete
}
“非智能”版本 - createAnimal() [工厂方法]
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 不使用删除(接受的答案看起来很优雅!!!它是某种“自定义释放器”吗?我不确定它
intrusive_ptr
在时间和空间效率方面如何比较)c++11 中的 intrusive_ptr(提到的公认答案
make_shared()
和enable_shared_from_this
(),但我不明白“但这不允许您使用不同的智能指针类型管理类型”部分)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()
似乎有一些问题)
参考
- [ EC++3 ]:有效的 C++:改进程序和设计的 55 种特定方法(第 3 版),Scott Meyers
- 第 7 条:在多态基类中声明虚拟析构函数
- [ MEC++1 ]:更有效的 C++:改进程序和设计的 35 种新方法(第 1 版),Scott Meyers
- 第 25 条:虚拟化构造函数和非成员函数
- 第 27 项:要求或禁止基于堆的对象。