RCObject
, which stands for "Reference Counted Object";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 of RCObject
]
[ 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;
RCObject
subclasses should be cloneable just like Cloneable
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 is autorelease
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 appropriate std::
or boost::
container. I mainly need a "std::vector
-like" container, a "std::set
-like" container and a "std::map
-like" container. The baseline is that
intrusive_ptr<RCObject> my_bear = v[10];
and
m["John"] = my_bear;
work as expected;
intrusive_ptr
instead of shared_ptr
, but I am open to both of them and even any other suggestions;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);RCObject
should be inherited from boost::noncopyable
privately;intrusive_ptr_add_ref()
and intrusive_ptr_release()
and how to implement them using Argument-Dependent Lookup (aka. Koenig Lookup);boost::atomic_size_t
with 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;
}
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.
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()
and enable_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 from zoo::Bear
alone, or should I make it extending both zoo::Bear
and intrusive_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)
boost::atomic
- Boost.org
make_shared
creates an instance of your class in the same allocation block as the reference counter. I am unsure why you think intrusive_ptr
will have better performance: it is great when there is already reference counting machinery you cannot remove, but this is not the case here.
For clone, I would implement it as a free function that takes a smart pojnter and returns same. It is a friend, and calls a private pure virtual clone method in base that returns a shared pointer to base, then does a fast smart pointer cast to shared pointer to derived. If you prefer clone as a method, use crtp to duplicate this (giving the private clone a name like secret_clone
). This gives you covariant smart pointer return types with little overhead.
Crtp with a range of base classes often has you pass both the base and derived classes in. The crtp class derives from base and has the usual self()
that returns derived.
Factory functions should return the smart pointer. You can use the custom deleter trick to get a pre-destroy methid call for last cleanup.
If you are completely paranoid, you can block most ways to get a raw pointer or reference to your class: block operator* on the smart pointer. Then the only route to the raw class is an explicit call to operator->
.
Another approach to consider is unique_ptr
and references to same. Do you need shared ownership and lifetime management? It does make some problems simpler (shared ownership).
Note that dangling weak pointers prevent the memory from make shared from bring recycled.
A serious downside to always using smart pointers is that you cannot have stack instances or instances directly inside containers. Both of these can be serious performance gains.
- Creating instances of
RCObject
subclasses on the stack should be prohibited ([MEC++1][mec++1] Item 27);
What's your rationale? MEC++ gives the example of "objects being able to commit suicide", which may make sense in the context of a game framework. Is that the case?
It should be possible to do with a smart enough smart pointer, if you insist on avoiding a simpler workaround.
Note that if that's the case, you'd probably want to also disallow creating arrays of such objects on the stack using new[]
- this also prevents from deleting a single one. You'll probably also want to disallow using RCObjects as sub-objects (members in other classes). This would mean that you're disallowing using RCObject values altogether and let client code only handle them through smart pointers.
- Declaring / returning raw pointers to
RCObject
(and subclasses) should be avoided (Unfortunately, I don't think there exists a way to enforce it by issuing compilation errors);
Then you're bound to have weak pointers to have a way of saying "I'm interested in this object but I'm not keeping it alive".
- Users subclassing
RCObject
should be able to write ["Factory Methods"][factory_method] for their subclasses. There should be no memory leak even if the returned value is ignored (not assigned to a variable).
Such function would return a temporary smart pointer object with reference count equal to 1. If this temporary isn't used to initialise another (thus further incrementing the ref count), it will clean the object up. You're safe.
- Instances of
RCObject
subclasses should be able to be contained in astd::
orboost::
container (or whatever is appropriate). I mainly need something similar tostd::vector
,std::set
andstd::map
;
This kind of disagrees with (3). If you insist on the objects having to be created individually on the heap and passed around through smart pointers (not as values), then you should also use containers of smart pointers.
- Due to performance considerations, I would like to use [
intrusive_ptr
][intrusive_ptr] instead of [shared_ptr
][shared_ptr], but I am open to both of them and even any other suggestions;
Aren't you optimising prematurely?
Also, I believe that using intrusive pointers takes away the possibility of using weak references - which you're very likely to need, as I mentioned before.
- I wonder whether
RCObject
should be inherited from [boost::noncopyable
][noncopyable] privately;
If you're disallowing variables of value type and providing a virtual Clone, then there's probably no need for a public copy constructor. You might make a private copy ctor and use it when defining Clone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With