Requirements
- 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).
More Information
- I am a beginner in Boost (Boost is so big that I need some time to be familiar with it. There may be existing out-of-the-box solutions, but it has a high chance that I am not aware of such a solution);
- Due to performance considerations, I would like to use
intrusive_ptr
instead ofshared_ptr
, but I am open to both of them and even any other suggestions; - I don't know whether
make_shared()
,allocate_shared()
,enable_shared_from_this
() could help (by the way,enable_shared_from_this
() seems not highly promoted in Boost - it could not even be found in the smart pointer main page); - I have heard of "writing custom allocators", but I am afraid it is too complicated;
- I wonder whether
RCObject
should be inherited fromboost::noncopyable
privately; - I couldn't find any existing implementation that fulfills all of my requirements;
- The framework is a game engine;
- The main target platforms are Android and iOS;
- I know
intrusive_ptr_add_ref()
andintrusive_ptr_release()
and how to implement them using Argument-Dependent Lookup (aka. Koenig Lookup); - I know how to use
boost::atomic_size_t
withboost:intrusive_ptr
.
Class Definitions
namespace zoo {
class RCObject { ... }; // Abstract
class Animal : public RCObject { ... }; // Abstract
class Bear : public Animal { ... }; // Concrete
class Panda : public Bear { ... }; // Concrete
}
"Non-smart" Version - createAnimal() [Factory Method]
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;
}
"Non-smart" Version - 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;
}
"Smart" Version [Incomplete!]
/* 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;
}
The above code ("Smart Version") shows the expected usage pattern. I am not sure whether this usage pattern follows best practices of using smart pointers or not. Please correct me if it doesn't.
Similar Questions
make shared_ptr not use delete (The accepted answer looks elegant !!! Is it some kind of "custom deallocator"s? I am not sure how it compares with
intrusive_ptr
in terms of time and space efficiency)intrusive_ptr in c++11 (The accepted answer mentioned
make_shared()
andenable_shared_from_this
(), but I do not understand the "but that doesn't allow you to manage the type using different smart pointer types" part)Is there a way to increase the efficiency of shared_ptr by storing the reference count inside the controlled object? (Still digesting)
intrusive_ptr: Why isn't a common base class provided? (The answers are not detailed enough)
Is this a valid use of intrusive_ptr? (I learnt something from it, but the focus of this question was on "passing a raw pointer to a function which accepts a smart pointer")
Reference counting with a generic intrusive pointer client (This one used "CRTP", but I'm afraid further subclassing will make me headache - should I make
zoo::Panda
extending fromzoo::Bear
alone, or should I make it extending bothzoo::Bear
andintrusive_base<zoo::Panda>
?)Embedded reference count with Boost shared_ptr (The accepted answer mentioned that while
std::enable_shared_from_this()
should be okay,boost::enable_shared_from_this()
seems to have some problems)
References
- [ EC++3 ]: Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) by Scott Meyers
- Item 7: Declare destructors virtual in polymorphic base classes
- [ MEC++1 ]: More Effective C++: 35 New Ways to Improve Your Programs and Designs (1st Edition) by Scott Meyers
- Item 25: Virtualizing constructors and non-member functions
- Item 27: Requiring or prohibiting heap-based objects.