要求
- I am writing a class called
RCObject
, which stands for "Reference Counted Object"; - The class
RCObject
should be abstract, serving as the base class of a framework (EC++3 Item 7); Creating instances of
RCObject
subclasses on the stack should be prohibited (MEC++1 Item 27);[ ADDED: ]
[ Assume
Bear
is a concrete subclass ofRCObject
][
C.E.
here means Compilation Error ]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: Declaring / returning raw pointers to
RCObject
(and subclasses) should be prohibited (Unfortunately, I don't think there exists a practical way to enforce it, i.e. triggering compilation errors when the users do not follow). See example source code in Item 3 above;- Instances of
RCObject
subclasses should be cloneable just likeCloneable
in Java. (MEC++1 Item 25); Users subclassing
RCObject
should be able to write "Factory Methods" for their subclasses. There should be no memory leak even if the returned value is ignored (not assigned to a variable). A mechanism which is close to this isautorelease
in Objective-C;[ ADDED: cschwan and Kos pointed out that returning a "smart-pointer-to-
RCObject
" is sufficient to fulfill the requirement. ]CLARIFIED: Instances of
RCObject
subclasses should be able to be contained in an appropriatestd::
orboost::
container. I mainly need a "std::vector
-like" container, a "std::set
-like" container and a "std::map
-like" container. The baseline is thatintrusive_ptr<RCObject> my_bear = v[10];
and
m["John"] = my_bear;
work as expected;
- The source code should be compilable using a C++98 compiler with limited C++11 support (Visual Studio 2008 and gcc 4.6, to be exact).
更多信息
- 我是Boost的初学者(Boost非常庞大,我需要一些时间来熟悉它。可能存在现成的解决方案,但我很可能不知道这样的解决方案);
- 由于性能考虑,我想使用
intrusive_ptr
而不是shared_ptr
,但我对两者甚至其他任何建议都持开放态度; - 我不知道
make_shared()
、allocate_shared()
、enable_shared_from_this
()是否有帮助(顺便说一句,enable_shared_from_this
()在Boost中似乎并没有得到很高的推广——在smart pointer主页上甚至找不到它); - 我听说过“编写自定义分配器”,但我担心它太复杂了;
- 我想知道是否应该将
RCObject
私有继承自boost::noncopyable
; - 我找不到任何现有的实现来满足我所有的要求;
- 框架是一个游戏引擎;
- 主要目标平台是Android和iOS;
- 我知道如何使用Argument-Dependent Lookup (aka. Koenig Lookup)实现
intrusive_ptr_add_ref()
和intrusive_ptr_release()
; - 我知道如何使用
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() [工厂方法]
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(被接受的答案看起来很优雅!!!它是某种“自定义解分配器”吗?我不确定它在时间和空间效率方面如何与
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]: 《Effective C++:改善程序与设计的55个具体做法》(第三版)作者:Scott Meyers
- 条款7:在多态基类中将析构函数声明为虚函数
- [MEC++1]: 《More Effective C++》:35种改进程序和设计的新方法(第一版),作者Scott Meyers
- 条款25:虚拟构造函数和非成员函数
- 条款27:要求或禁止基于堆的对象。