Archived

This topic is now archived and is closed to further replies.

Release() and how that works...

This topic is 5777 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Okay, I was wondering, how the Release() thing works found so often in the DirectX Interfaces...I noticed, you never ''delete'' them. You just Release() and make them NULL. I made a class like that and did some tests. Anyone else know if this is actually valid or not? I didn''t include the #includes though...
  
class MemTestR
{
public:
   MemTestR(): m_ref(1) {};

   int Release()
   {
      int tmp = --m_ref;

      if(m_ref<=0)
         delete(this);

      return(tmp);
   }

   int m_ref;
   int m_x, m_y;
};

///----------------------------------------------------------------------------

//

//

// main()

//

//

///----------------------------------------------------------------------------


int main()
{
   {
      MemTestR * mtr = NULL;

      mtr = new MemTestR;

      mtr->m_x = 0;
      mtr->m_y = 0;

      printf("\nX:%i\nY:%i\nRef:%i\n", mtr->m_x, mtr->m_y, mtr->m_ref);

      int tmp = mtr->Release();

      printf("\nX:%i\nY:%i\nRef:%i\n", mtr->m_x, mtr->m_y, tmp);//mtr->m_ref);

   }

   /// doesn''t work if you don''t make it a pointer...

   {
      MemTestR mtr;

      mtr.m_x = 0;
      mtr.m_y = 0;

      printf("\nX:%i\nY:%i\nRef:%i\n", mtr.m_x, mtr.m_y, mtr.m_ref);

      int tmp = mtr.Release();

      printf("\nX:%i\nY:%i\nRef:%i\n", mtr.m_x, mtr.m_y, tmp);//mtr->m_ref);

   }

   printf("\n\nDone\n");

   return(0);
}

  

Share this post


Link to post
Share on other sites
ok, the release function has one purpose and one purpose alone, to release directdraw (possibly D3D) surfaces, before you exit the program you have to "release" all of the surfaces you created.

,Matt

-= kill one your a murderer, kill thousands your a conquerer =-

Edited by - samosa on February 17, 2002 10:32:11 PM

Share this post


Link to post
Share on other sites
I would recommend you to have a look at the "reference counting" subject in a good C++ book, like Scott Meyers'' "Effective C++"... Also, look up COM in the MSDN.



Laurent - http://www.lafaqmfc.com/
My little game: http://www.lafaqmfc.com/starshooter.htm

Share this post


Link to post
Share on other sites
Thank you for you input, samosa.

However, I am aware of the reason of Release(). I know it ''releases'' the surfaces and other things. But in releases a surface, dosn''t that deallocate the memory? And therefore call delete or free or whatever on the object itself? I would like to make a class, or interface, or some other object that can delete itself when it needs to (the reference count has gone down to 0).

Or does DirectX just create all the stuff somewhere else, and calling Release() just delete that stuff where it exists? I am really interested in what they did you accomplish that. It seems to work in the test program I posted up here.

Cocyen

Share this post


Link to post
Share on other sites
Thanks Laurent. I will definitely check Effective C++ out. It seems like a great idea, so you can have a ton of things pointing to 1 object and each one releases it and it finally does away with itself when it''s count is 0.

By the way...you mentioned COM. While I know DirectX cuddles up to that...is the Release() something that you couldn''t do without COM or what? I am still going to check the book out, but I''d rather steer away from programming something with COM.

Cocyen

Share this post


Link to post
Share on other sites
I thought everything in a program was destroyed as soon as you quit the application... I still use the Release() functions, but is it really needed? Or is that why I kept getting the same palette when I ran the program again even when I never set it in the first place. Bah! Whatever. BSTS.

Share this post


Link to post
Share on other sites
Well, that is amusing about the palette...however, if you have like a big 3D game and huge levels with 50 or so MB of data, chances are you are going to want to free some of that data before you load in the next level (or stream in parts of another area). So you will be releasing and creating the data over and over. And if you never cleared the data it would all just pile up and crash the program or something.


- Cocyen

Share this post


Link to post
Share on other sites
COM uses reference counting, which is why you call Release() (which will call delete if necessary), but there is no reason you cannot add reference counting in your own classes.

It is COM which needs Release, not the other way round.

By the way, since we are talking about ref-counting, you can implement your own smart pointer class which would call Release automatically when you assign them another value.

e.g. (just a proof of concept, probably full of bugs)

  
template<class Pointee> // Make a pointer to Pointee

SmartPointer
{
private:
Pointee* ptr;
public:
SmartPointer() : ptr( NULL ) {};
SmartPointer( Pointee* _ptr, bool addref = true )
: ptr( _ptr )
{
// COM objects already have their reference count

// incremented when you get them, so, if you want

// the smart pointer to ''take over'', the reference

// stored in the C pointer, you do not increment

// again (addref=false).


if( ptr && addref ) ptr->AddRef();
}

~SmartPointer()
{
if( ptr ) ptr->Release();
}

SmartPointer( const SmartPointer<Pointee>& other )
: ptr( other.ptr )
{
if( other.ptr ) other.ptr->AddRef();
}

SmartPointer<Pointee>&
operator=( const SmartPointer<Pointee>& other )
{
if( ptr == other.ptr )
return *this;

// Note: always AddRef before Release


if( other.ptr ) other.ptr->AddRef();
if( ptr ) ptr->Release()
ptr = other.ptr

return *this;
}

Pointee& operator*() { return *ptr };
}

Share this post


Link to post
Share on other sites
Hmmm, Samosa is wrong on this one.

DX is based on COM, all Release calls are COM, and not related specifically to surfaces or whatever the object can be.

I had no time to try to compile Fruny''s class, but this one looks very good to me It shows nicely what reference counting is and how good it can be.


Laurent - http://www.lafaqmfc.com/
My little game: http://www.lafaqmfc.com/starshooter.htm

Share this post


Link to post
Share on other sites
That does look like a good class. I haven''t worked with templates much though. I''m going to get the Service Packs first since I hear that is a place MSVC++ has a bit of problems with. However...what about the Release() method, could someone show me an example how one inside a class. Or if this one is valid (that I posted earlier)?

  
class MemTestR
{
public:
MemTestR(): m_ref(1) {};
int Release()
{
int tmp = --m_ref;
if(m_ref<=0)
delete(this);
return(tmp);
}
int m_ref;
int m_x, m_y;
};

Share this post


Link to post
Share on other sites
Your Release is fine, though it''s useless without an AddRef. But yeah, that''s how you''d do it.

I wrote a smart pointer class, although I eventually decided not to bother using it. But it was this:

  
template <class Type> class pointer
{
public:

pointer ( )
{
m_Object = NULL;
}

pointer ( Type* Object )
{
m_Object = Object;

if( m_Object )
m_Object->AddRef( (object**)&m_Object );
}

~pointer ( )
{
if( m_Object )
m_Object->Release( );
}

inline operator = ( Type* Object )
{
if( m_Object )
m_Object->Release( );

m_Object = Object;

if( m_Object )
m_Object->AddRef( (object**)&m_Object );
}

inline Type* operator -> ( ) const
{
return m_Object;
}

inline Type* operator () ( ) const
{
return m_Object;
}

inline operator Type* ( ) const
{
return m_Object;
}

inline bool operator == ( Type* Object ) const
{
return m_Object == Object;
}

inline bool operator != ( Type* Object ) const
{
return m_Object != Object;
}

inline operator bool ( ) const
{
return m_Object ? true : false;
}

private:

Type* m_Object;
};


The object had to be derived from a base object that had the AddRef and Release methods. All my classes are derived from a common base class (except pointer) so I could do things like this.

It would be used like this:
  
pointer<OpenGL> pGL = new OpenGL; // Create it.

pGL->SetDisplayMode( 640, 480, 16 ); // Do something.

pGL = NULL; // This releases the object.



~CGameProgrammer( );

Share this post


Link to post
Share on other sites
CGameProgrammer, there is a problem with your assignment operator (just what I had warned about in my code)

if Object == m_Object, the fact that you call release first may cause the object to be destroyed.


// if m_Object == Object, release Object, which may be deleted
if( m_Object ) m_Object->Release( );
// This line would basically be a no-op
m_Object = Object;
// m_Object could now be a dangling pointer... calling AddRef would segfault.
if( m_Object )
m_Object->AddRef( (object**)&m_Object );

Share this post


Link to post
Share on other sites
Actually, it won''t, I don''t think. Unless you say pGL = pGL of course. I suppose it''s not a bad idea checking to make sure m_Object isn''t the same as Object, but since only bad programming would cause that situation to arise, I wouldn''t waste a conditional jump.

~CGameProgrammer( );

Share this post


Link to post
Share on other sites
Probably because they may be used in bad programming. When making public libraries like that, you want to be extra-careful. But for your own code, you can make optimizations if you know it won''t be a problem for you.

~CGameProgrammer( );

Share this post


Link to post
Share on other sites
It is critical to ensure correctness first, then speed.
If you are working in any kind of professional situation, you don''t know who will go through your code after you.

Share this post


Link to post
Share on other sites
I agree with Fruny. In any event, it''s always better to waste a jump rather than to spend half a day debugging your program when you in fact do assign a pointer to itself. If you are extremely concerned with performance, at least put an ASSERT in there. However, I doubt that you''ll see any speed difference just because you don''t check this case.

Correctness is also why I wrap every single DX interface method I use, and check all HRESULTs. I know that DrawPrimitive doesn''t fail unless you give it a NULL pointer, but I once had a problem even with it.

Share this post


Link to post
Share on other sites
#include "atlbase.h"
//
#include "d3d8.h"
#pragma comment( lib, "d3d8.lib" )
#pragma comment( lib, "dxguid.lib" )
//...
CComPtr<IDirect3D8 *> pd3d;
//...
int WINAPI WInMain(..)
{
pd3d.Attach( ::Direct3D8Create(D3D_SDK_VERSION, ...) );
// no need to call release; CComPtr does that automatically
// could also have used CComQIPtr which supports QueryInterface
}


I wanna work for Microsoft!
[ GDNet Start Here | GDNet Search Tool | GDNet FAQ | MS RTFM [MSDN] | SGI STL Docs | Google! ]
Thanks to Kylotan for the idea!

Share this post


Link to post
Share on other sites
Thanks everybody for all the input...even though it seems like the subject changed a bit. I will probably just use regular pointers though. Not sure...anyway, I guess it seems okay to have a method in a class delete itself like in my Release() method, as long as it''s a pointer.

Cocyen

Share this post


Link to post
Share on other sites