Crystal Space
Welcome, Guest. Please login or register.
December 22, 2014, 11:52:45 am

Login with username, password and session length
Search:     Advanced search
9032 Posts in 2046 Topics by 9302 Members
Latest Member: Janrdym
* Home Help Search Login Register
+  Crystal Space
|-+  Crystal Space Development
| |-+  General Crystal Space Discussion
| | |-+  Need Help Understanding Smart Pointers/Abstract Classes
« previous next »
Pages: [1] Print
Author Topic: Need Help Understanding Smart Pointers/Abstract Classes  (Read 2538 times)
yoshi
Newbie
*
Posts: 40


View Profile WWW
« on: July 29, 2005, 08:26:15 am »

Hi, it's been a while.

(For those who do not want to read all this, see summary at the bottom).

I read the CS Manual section on how to correctly use smart pointers, but I still have questions about it.  I've had a few years of experience with C++ but no proper education in computer science, so I don't know much about advanced programming topics like reference counting or abstract classes, two things which CS seems to use extensively.

I understand that you use csRef when you want to own a reference to a reference-counted object (I have a vague understanding of what reference counting is, courtesy of wikipedia).  So I figure, pointers to important things like iEngine, iGraphics3D, iView, etc. should be csRefs.  Fine.  So I set out to create a wrapper class for the camera, containing normal pointers to iCamera and iCameraPosition as private members.  I figure, "Hey, they're not big, important objects like the engine, so being normal pointers should suffice."  At some point I assigned my iCamera pointer to a new iCamera.  When I went to compile it, MSVC flags an error saying abstract classes cannot be instantiated.  Baffled by this, I look up what an abstract class is, and from what I understand, they're classes with pure virtual functions in them.

I think "Wait a minute... that's like, <exaggeration>every</exaggeration> CS class there is."

Then the terrible truth slowly dawns upon me, and it's like one of those sunny days when a huge cloud blocks the sun and everything grows dim.  Come to think of it, I've NEVER explicity declared a new iAnything and everything just worked.

I know csRefs take care of initialization stuff (or something) for you, but that still doesn't mean you can't do things the old way, right?  In an example in the manual, it has a normal iEngine pointer, and then upon cleanup, DecRef() is being invoked.  I thought the incref/decref stuff was for reference counting maintenance.  Why is a normal pointer having to decref?

Bottom Line (Summary): It doesn't really seem like CS's plethora of abstract classes can be normally pointed to (because of the "abstract classes cannot be instantiated" error), but I don't want to giddily use csRefs for everything (actually I do because I'm lazy, but it's probably not a good idea).  So if someone could clarify these issues for me, that would be greatly appreciated.  Thanks in advance.

- yoshi (PS: CS rocks my socks)
Logged
jorrit
Administrator
Hero Member
*****
Posts: 1706


View Profile
« Reply #1 on: July 29, 2005, 07:36:06 pm »

Hi, it's been a while.

(For those who do not want to read all this, see summary at the bottom).

I read the CS Manual section on how to correctly use smart pointers, but I still have questions about it.  I've had a few years of experience with C++ but no proper education in computer science, so I don't know much about advanced programming topics like reference counting or abstract classes, two things which CS seems to use extensively.

I understand that you use csRef when you want to own a reference to a reference-counted object (I have a vague understanding of what reference counting is, courtesy of wikipedia).  So I figure, pointers to important things like iEngine, iGraphics3D, iView, etc. should be csRefs.  Fine.  So I set out to create a wrapper class for the camera, containing normal pointers to iCamera and iCameraPosition as private members.  I figure, "Hey, they're not big, important objects like the engine, so being normal pointers should suffice."  At some point I assigned my iCamera pointer to a new iCamera.  When I went to compile it, MSVC flags an error saying abstract classes cannot be instantiated.  Baffled by this, I look up what an abstract class is, and from what I understand, they're classes with pure virtual functions in them.

It does not matter if an object is 'important' or not for reference counting. What matters is if you want to keep a reference to that object. If you keep a reference to an object you prevent the object from being removed. If you use a normal pointer to an object it may be possible for the object to be removed and the pointer will become invalid then (point to memory that is no longer that object).

As to not being able to do 'new iCamera'. That's normal. iCamera is a class that only has pure virtual functions in it. That means that it has no implementation. Only function declarations. Because it has no implementation it is not possible to do 'new iCamera'. The only way to create a camera is to use engine->CreateCamera() like this:

Code:
csRef<iCamera> camera;
camera = engine->CreateCamera ();

You must use a csRef here because CreateCamera() returns a csPtr. And you can only assign those to csRef's. The reason that it must be a csRef is that you need to keep a real reference to the camera to prevent it from being deleted.


Quote
I think "Wait a minute... that's like, <exaggeration>every</exaggeration> CS class there is."

Then the terrible truth slowly dawns upon me, and it's like one of those sunny days when a huge cloud blocks the sun and everything grows dim.  Come to think of it, I've NEVER explicity declared a new iAnything and everything just worked.

I know csRefs take care of initialization stuff (or something) for you, but that still doesn't mean you can't do things the old way, right?  In an example in the manual, it has a normal iEngine pointer, and then upon cleanup, DecRef() is being invoked.  I thought the incref/decref stuff was for reference counting maintenance.  Why is a normal pointer having to decref?

csRef is just a conveniance class on top of normal reference counting. Many objects in CS support reference counting by implementing an IncRef() and a DecRef() routine. You can use normal pointers and call IncRef/DecRef manually to do reference counting but that is not recommended as it is error-prone. Much better is it to use csRef which calls IncRef/DecRef automatically.

Quote
Bottom Line (Summary): It doesn't really seem like CS's plethora of abstract classes can be normally pointed to (because of the "abstract classes cannot be instantiated" error), but I don't want to giddily use csRefs for everything (actually I do because I'm lazy, but it's probably not a good idea).  So if someone could clarify these issues for me, that would be greatly appreciated.  Thanks in advance.

- yoshi (PS: CS rocks my socks)


I hope my explanation helps a bit. If not, just ask here.

Greetings,
Logged
yoshi
Newbie
*
Posts: 40


View Profile WWW
« Reply #2 on: July 29, 2005, 10:21:27 pm »

Thanks for the explanation; it was helpful, but I still have some questions.

1. So basically, it's best to use csRefs for all CS interface classes?
2. In the documentation, it says that a function can return a normal pointer, and if you assign that to a normal pointer, "then you will not own a reference to the returned object."  What does that mean?  If I do " iCamera * c = view->getCamera(); " then the pointer c won't be able to do anything with the iCamera members?  Or does it just mean that the c pointer won't be counted as one of the owners of that specific camera?
3. What happens if I get a normal pointer from a function, assign it to a normal pointer, and then try to delete it?  In other words, what will happen if I do " iCamera * c = view->getCamera(); delete c; "?  Is that something that should never be done?
4. Just to make sure, if I have a csRef to something, and then assign a different csRef of the same type to the first one, then it takes care of DecRef()-ing the old data and IncRef()-ing the new data, right?
5. I keep thinking of more things that I don't understand.  So suppose I have a class that contains a csRef of iCamera as a private member.  I want to create a member function for this calss called GetCamera that returns a normal iCamera pointer.  In the manual, it says you shouldn't do
Code:
iFoo* MyFunction ()
{
  csRef<iFoo> foo = ...;
  ...
  return foo;
}
but that's only because foo exists only within the scope of that function, right?  I should be able to do
Code:
iCamera * MY_CAMERA::GetCamera(void)
{
   return m_Camera;
}
safely because m_Camera still exists as a private member of the class, right?

Again, thanks for the help.

edit 1 & 2: added more questions.. heh
edit 3: another question, sorry.. this stuff can get pretty confusing
« Last Edit: July 29, 2005, 11:37:10 pm by yoshi » Logged
jorrit
Administrator
Hero Member
*****
Posts: 1706


View Profile
« Reply #3 on: July 30, 2005, 06:10:30 am »

Thanks for the explanation; it was helpful, but I still have some questions.

1. So basically, it's best to use csRefs for all CS interface classes?

You can do that but it is not really required to do that because in many cases the engine itself will keep track of the reference. For example, when you do this code:

Code:
iSector* sector = engine->FindSector ("room");

In most cases it is not needed to use csRef since the engine keeps its own internal reference to that sector anyway. Usually you know the lifetime of that sector so there is no problem. You can use a csRef here though if you want.

There is one potential problem with csRef's and that is that they actually prevent objects from being deleted and sometimes that is not good. For example if you have A with a csRef to B and B with a csRef to A then you have a circular reference that can never be deleted automatically.

There in one case where you must use a csRef and that is when a function returns a csPtr. In that case you have no choice but to use a csRef (or a csWeakRef but lets leave those out of the discussion for now smiley If a function returns a csRef then you should also assign it to a csRef.

Quote
2. In the documentation, it says that a function can return a normal pointer, and if you assign that to a normal pointer, "then you will not own a reference to the returned object."  What does that mean?  If I do " iCamera * c = view->getCamera(); " then the pointer c won't be able to do anything with the iCamera members?  Or does it just mean that the c pointer won't be counted as one of the owners of that specific camera?

It means that the c pointer will not be counted as one of the owners. You can still use it.

Quote
3. What happens if I get a normal pointer from a function, assign it to a normal pointer, and then try to delete it?  In other words, what will happen if I do " iCamera * c = view->getCamera(); delete c; "?  Is that something that should never be done?

Never delete reference counted objects. Always let reference counting do its work. Something like 'delete c' when c is an iCamera is illegal.

Quote
4. Just to make sure, if I have a csRef to something, and then assign a different csRef of the same type to the first one, then it takes care of DecRef()-ing the old data and IncRef()-ing the new data, right?

Yes

Quote
5. I keep thinking of more things that I don't understand.  So suppose I have a class that contains a csRef of iCamera as a private member.  I want to create a member function for this calss called GetCamera that returns a normal iCamera pointer.  In the manual, it says you shouldn't do
Code:
iFoo* MyFunction ()
{
  csRef<iFoo> foo = ...;
  ...
  return foo;
}
but that's only because foo exists only within the scope of that function, right?  I should be able to do
Code:
iCamera * MY_CAMERA::GetCamera(void)
{
   return m_Camera;
}
safely because m_Camera still exists as a private member of the class, right?

Yes

Greetings,
Logged
Pages: [1] Print 
« previous next »
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.2 | SMF © 2006-2007, Simple Machines LLC Valid XHTML 1.0! Valid CSS!
Page created in 8.444 seconds with 17 queries.