c++ - Abstract base class for use with smart pointers (intrusive_ptr) - handling inheritance, polymorphism, cloneability and returning from factory methods -
requirements
- i writing class called
rcobject
, stands "reference counted object"; - the class
rcobject
should abstract, serving base class of framework (ec++3 item 7); creating instances of
rcobject
subclasses on stack should prohibited (mec++1 item 27);[ added: ]
[ assume
bear
concrete subclass ofrcobject
][
c.e.
here means compilation error ]bear b1; // triggers c.e. (by using mec++1 item 27) bear* b2; // not allowed no way 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 no way trigger c.e. intrusive_ptr<bear> m_b3; // recommended };
clarified: declaring / returning raw pointers
rcobject
(and subclasses) should prohibited (unfortunately, don't think there exists practical way enforce it, i.e. triggering compilation errors when users not follow). see example source code in item 3 above;- instances of
rcobject
subclasses should cloneablecloneable
in java. (mec++1 item 25); users subclassing
rcobject
should able write "factory methods" subclasses. there should no memory leak if returned value ignored (not assigned variable). mechanism closeautorelease
in objective-c;[ added: cschwan , kos pointed out returning "smart-pointer-to-
rcobject
" sufficient fulfill requirement. ]clarified: instances of
rcobject
subclasses should able contained in appropriatestd::
orboost::
container. need "std::vector
-like" container, "std::set
-like" container , "std::map
-like" container. baseline thatintrusive_ptr<rcobject> my_bear = v[10];
and
m["john"] = my_bear;
work expected;
- the source code should compilable using c++98 compiler limited c++11 support (visual studio 2008 , gcc 4.6, exact).
more information
- i beginner in boost (boost big need time familiar it. there may existing out-of-the-box solutions, has high chance not aware of such solution);
- due performance considerations, use
intrusive_ptr
instead ofshared_ptr
, open both of them , other suggestions; - i don't know whether
make_shared()
,allocate_shared()
,enable_shared_from_this
() (by way,enable_shared_from_this
() seems not highly promoted in boost - not found in smart pointer main page); - i have heard of "writing custom allocators", afraid complicated;
- i wonder whether
rcobject
should inheritedboost::noncopyable
privately; - i couldn't find existing implementation fulfills of requirements;
- the framework game engine;
- the main target platforms android , ios;
- i know
intrusive_ptr_add_ref()
,intrusive_ptr_release()
, how implement them using argument-dependent lookup (aka. koenig lookup); - i know how use
boost::atomic_size_t
boost: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) { // wish call result->autorelease() @ 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 stack should fail following // method described in 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 write factory method? should 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 stack should fail following // method described in book mec++1 item 27. unfortunately, there // doesn't exist way ban user declaring raw pointer // rcobject (and subclasses), relies self discipline... // //zoo::bear b; // should fail //zoo::panda p; // should fail //zoo::bear* pb; // no way ban //zoo::panda* pp; // no way ban // part 2 - object assignment /* ... skip (exactly same "non-smart") ... */ // part 3 - cloning /* todo: how write this? */ return 0; }
the above code ("smart version") shows expected usage pattern. not sure whether usage pattern follows best practices of using smart pointers or not. please correct me if doesn't.
similar questions
make shared_ptr not use delete (the accepted answer looks elegant !!! kind of "custom deallocator"s? not sure how compares
intrusive_ptr
in terms of time , space efficiency)intrusive_ptr in c++11 (the accepted answer mentioned
make_shared()
,enable_shared_from_this
(), not understand "but doesn't allow manage type using different smart pointer types" part)is there way increase efficiency of shared_ptr storing reference count inside controlled object? (still digesting)
intrusive_ptr: why isn't common base class provided? (the answers not detailed enough)
is valid use of intrusive_ptr? (i learnt it, focus of question on "passing raw pointer function accepts smart pointer")
reference counting generic intrusive pointer client (this 1 used "crtp", i'm afraid further subclassing make me headache - should make
zoo::panda
extendingzoo::bear
alone, or should make extending bothzoo::bear
,intrusive_base<zoo::panda>
?)embedded reference count boost shared_ptr (the accepted answer mentioned while
std::enable_shared_from_this()
should okay,boost::enable_shared_from_this()
seems have problems)
references
- [ ec++3 ]: effective c++: 55 specific ways improve programs , designs (3rd edition) scott meyers
- item 7: declare destructors virtual in polymorphic base classes
- [ mec++1 ]: more effective c++: 35 new ways improve programs , designs (1st edition) scott meyers
- item 25: virtualizing constructors , non-member functions
- item 27: requiring or prohibiting heap-based objects.
articles
make_shared
creates instance of class in same allocation block reference counter. unsure why think intrusive_ptr
have better performance: great when there reference counting machinery cannot remove, not case here.
for clone, implement free function takes smart pojnter , returns same. friend, , calls private pure virtual clone method in base returns shared pointer base, fast smart pointer cast shared pointer derived. if prefer clone method, use crtp duplicate (giving private clone name secret_clone
). gives covariant smart pointer return types little overhead.
crtp range of base classes has pass both base , derived classes in. crtp class derives base , has usual self()
returns derived.
factory functions should return smart pointer. can use custom deleter trick pre-destroy methid call last cleanup.
if paranoid, can block ways raw pointer or reference class: block operator* on smart pointer. route raw class explicit call operator->
.
another approach consider unique_ptr
, references same. need shared ownership , lifetime management? make problems simpler (shared ownership).
note dangling weak pointers prevent memory make shared bring recycled.
a serious downside using smart pointers cannot have stack instances or instances directly inside containers. both of these can serious performance gains.
Comments
Post a Comment