C++ Refcounting design
-
> no - good point, I suppose it should be protected So it doesn't have to be virtual... Note that you're always dealing with object of type Foo in Foo::create: static Foo* create( int count ) { Foo* result = new Foo( count ); result->init(); //calls Foo::init even if init isn't virtual return result; } In fact, if you don't have two or more versions of Foo::create, you could place all init functionality inside create. Personally, I'd move the parameters from the constructor to non-virtual init: static Foo* create( int count ) { Foo* result = new Foo(); result->init(count); return result; } The only thing that constructor does in this scenario is initializing vtable pointer (this of course is done automatically for you). 'Real' init would be performed in init() itself. Tomasz Sowinski -- http://www.shooltz.com.pl
Well the init function is virtual so that base class initialization will take place in one common place. No arguments are needed for it - the constructor will initialize member variables (set pointers to NULL, etc )and then init does the rest. All classes will be a part of a single rooted hierarchy.
-
I'm also not sure why do you need free() function. Isn't virtual destructor enough? I'm fully aware that virtual functions are called non-virtually from d'tor, but can't see any serious issue here. Do you expect problems related to the order of destructor calls? Tomasz Sowinski -- http://www.shooltz.com.pl
Normally I'd agree with you but I have some stuff that needs to have virtual clean up methods that need to be called prior to the destructor to work right. That is the idea behind the free() call, a single place where the virtual clean up methods can get called prior to the destructor, sort of like the FinalConstruct()/FinalDestruct() of ATL.
-
Well the init function is virtual so that base class initialization will take place in one common place. No arguments are needed for it - the constructor will initialize member variables (set pointers to NULL, etc )and then init does the rest. All classes will be a part of a single rooted hierarchy.
Sorry, but it doesn't matter if init is virtual or not. You will have to explicitly call base::init in derived::init. Virtualness has nothing to do with it. Tomasz Sowinski -- http://www.shooltz.com.pl
-
Sorry, but it doesn't matter if init is virtual or not. You will have to explicitly call base::init in derived::init. Virtualness has nothing to do with it. Tomasz Sowinski -- http://www.shooltz.com.pl
in your implementation of init() yes, I understand that, what I was referring to was the desired behaviour of being able to create and instance of some object and then call init() on that object, all in the constext of a ::create() call. This would then chain all the functionality together in the virtual init()'s.
-
in your implementation of init() yes, I understand that, what I was referring to was the desired behaviour of being able to create and instance of some object and then call init() on that object, all in the constext of a ::create() call. This would then chain all the functionality together in the virtual init()'s.
> This would then chain all the functionality together > in the virtual init()'s. Are you implying that making member function virtual does 'chaining'? It's not the case, at least with C++ - you'll have to chain manually inserting base::init in derived::init. Virtual makes no difference here. If you plan to create Foo only in static Foo::create (as you've mentioned before in this thread), then your init() doesn't have to be virtual at all. Inside Foo::create you have pointer to well-known Foo type, and there's no need to make init() virtual. Tomasz Sowinski -- http://www.shooltz.com.pl
-
> This would then chain all the functionality together > in the virtual init()'s. Are you implying that making member function virtual does 'chaining'? It's not the case, at least with C++ - you'll have to chain manually inserting base::init in derived::init. Virtual makes no difference here. If you plan to create Foo only in static Foo::create (as you've mentioned before in this thread), then your init() doesn't have to be virtual at all. Inside Foo::create you have pointer to well-known Foo type, and there's no need to make init() virtual. Tomasz Sowinski -- http://www.shooltz.com.pl
again, I understand the implementation techniques for virtual functions. However, NOT making the init() function virtual means you will not be able to do have this behaviour - rememeber that Foo will have to derive from some base class. i.e. class Base { virtual void init() { //do something } static Base* create() { Base* result = new Base(); result->init(); return result; } }; class Foo : public Base { virtual void init() { Base::init(); //do some more stuff } static Foo* create() { Foo* result = new Foo(); result->init(); return result; } }; so calling Base* b = Base::create() means Base::init() gets called while Foo* f = Foo::create() results in Base::init() and Foo::init() getting called. If init() was not virtual this would not work, correct ?
-
again, I understand the implementation techniques for virtual functions. However, NOT making the init() function virtual means you will not be able to do have this behaviour - rememeber that Foo will have to derive from some base class. i.e. class Base { virtual void init() { //do something } static Base* create() { Base* result = new Base(); result->init(); return result; } }; class Foo : public Base { virtual void init() { Base::init(); //do some more stuff } static Foo* create() { Foo* result = new Foo(); result->init(); return result; } }; so calling Base* b = Base::create() means Base::init() gets called while Foo* f = Foo::create() results in Base::init() and Foo::init() getting called. If init() was not virtual this would not work, correct ?
> If init() was not virtual this would not work, correct ? Not correct. It would work without any problems as non-virtual. Note that the local variable 'result' in static Foo::create is of type Foo, not Base. The compiler will insert the direct call to Foo::init if init would be nonvirtual. If init() would be virtual, the generated code would use the vtable to jump indirectly to Foo::init. You'll get identical results. Tomasz Sowinski -- http://www.shooltz.com.pl
-
> If init() was not virtual this would not work, correct ? Not correct. It would work without any problems as non-virtual. Note that the local variable 'result' in static Foo::create is of type Foo, not Base. The compiler will insert the direct call to Foo::init if init would be nonvirtual. If init() would be virtual, the generated code would use the vtable to jump indirectly to Foo::init. You'll get identical results. Tomasz Sowinski -- http://www.shooltz.com.pl
OK now I see what you're saying, which makes sense I guess, and I suppose there is a small performance increase. Learn some thing new every day :) Thanks
-
Hi, I have a design I am considering implementing and I was wondering if I could get some feedback on it. Basically I am considering implementing a refcounting scheme (nothing too difficult there) as well as an instantiation techniques that works as follows: All classes that need to be allocated on the heap will have a virtual method called init(). init() provides a common place to initialize variables, etc. All classes provide a static method called create() that returns a new instance of the class. the create() method should mirror the constructor for the class as it will call this constructor when creating a new instance for the class. Example: class Foo { Foo(); Foo( int count ); static Foo* create() { Foo* result = new Foo(); result->init(); return result; } static Foo* create( int count ) { Foo* result = new Foo( count ); result->init(); return result; } }; This allows for virtual init function to be called, allowing single step creation, which is convenient for many kinds of classes, at least in what I am working on, so the syntax is now the following for creating things on the heap: Foo* f = Foo::create(); instead of Foo* f = new Foo(); The base class would provide addRef(), release(), and getRefCount() for performing refcounting. The base class would also have two more functions. A protected virtual function called destroy() that would contain common termination code. Another public method called free() that would call destroy() and then delete this to destroy the object. the release() method would call free() (instead of delete this) when the refcount dropped to 0. So to completely destroy an object on the heap where you "own" that object (i.e. an object that doesn't get used by anyone else but the class instantiating it) you would do the following: Foo* f.... //stuff happens //later on... f->free(); To simply release the object the sfollowing syntax is used: f->release(); The advantage (or at least what I am thinking is the advantage), is the ability to have a common virtual function for both initialization ( init() ) and clean up ( destroy() ) that will safely get called, since constructors and destructors cannot call virtual functions in their implementation (well they can, they just don't behave like you'd expect them ). In addition, a ref counting scheme is in place to allow potential COM features in the library. Hope all this is reasonably clear, and I look forward to any
You may be interested to look at a similar scheme that is used by the open source package Visualization Toolkit. They use a scheme like this and have put a lot of thought into making it robust and fast. They point out that you can do a lot of good optimizing if you can be sure that all objects are allocated on the heap (as opposed to global variable memory or on the stack), so they use static member functions
New()
andDelete()
similar to yourcreate()
anddestroy()
. -
Hi, I have a design I am considering implementing and I was wondering if I could get some feedback on it. Basically I am considering implementing a refcounting scheme (nothing too difficult there) as well as an instantiation techniques that works as follows: All classes that need to be allocated on the heap will have a virtual method called init(). init() provides a common place to initialize variables, etc. All classes provide a static method called create() that returns a new instance of the class. the create() method should mirror the constructor for the class as it will call this constructor when creating a new instance for the class. Example: class Foo { Foo(); Foo( int count ); static Foo* create() { Foo* result = new Foo(); result->init(); return result; } static Foo* create( int count ) { Foo* result = new Foo( count ); result->init(); return result; } }; This allows for virtual init function to be called, allowing single step creation, which is convenient for many kinds of classes, at least in what I am working on, so the syntax is now the following for creating things on the heap: Foo* f = Foo::create(); instead of Foo* f = new Foo(); The base class would provide addRef(), release(), and getRefCount() for performing refcounting. The base class would also have two more functions. A protected virtual function called destroy() that would contain common termination code. Another public method called free() that would call destroy() and then delete this to destroy the object. the release() method would call free() (instead of delete this) when the refcount dropped to 0. So to completely destroy an object on the heap where you "own" that object (i.e. an object that doesn't get used by anyone else but the class instantiating it) you would do the following: Foo* f.... //stuff happens //later on... f->free(); To simply release the object the sfollowing syntax is used: f->release(); The advantage (or at least what I am thinking is the advantage), is the ability to have a common virtual function for both initialization ( init() ) and clean up ( destroy() ) that will safely get called, since constructors and destructors cannot call virtual functions in their implementation (well they can, they just don't behave like you'd expect them ). In addition, a ref counting scheme is in place to allow potential COM features in the library. Hope all this is reasonably clear, and I look forward to any
Looks like someone is building a smart pointer.;)
This guy wrote a nice smart pointer using strong and weak refrences to compensate for circular refrences. Check it out if your interested : http://www.jelovic.com/articles/cpp_without_memory_errors_slides.htm