The "interface" classes.

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • The "interface" classes.

      Hello all. I'm totally new here. In fact I'm rather new to C++. (I used to dwell in the realm of VB) After working on "Phantasm Souls" for a year or so (I'm not sure if anyone here has heard of it) I decided to move over to C++.

      I have a fairly strong understanding of it, thanks to Game Coding Complete 2nd Edition and Learning C++ (by Neill Graham) and a few internet resources.

      My only question I've been unable to find an answer for, and it's probably really simple, is why Mr Mike creates abstract base classes for all the major parts of his project, even if only one other class will inherit from it. Can't the inheriting class just BE the top of the heirarchical tree?

      Is this for the sake of efficiency, the sake of quickly modifiable code or is it just a known C++ coding practice?

      A good example is the iAudio and iAudioBuffer that are inherited by cAudio and cAudioBuffer who only perform the generic parts of the audio code before being inherited by cDirectSoundAudio and cDirectSoundAudioBuffer who do the sound API specific parts. To my mind cAudio and cAudioBuffer could do it all without the need for the "interface" Abstract Base Classes.

      I hope I've explained what I'm confused about clearly :)

      Steve
    • my understanding of it is this:

      it provides a common interface for all code to interact with. in the book, where he only shows one or two inhereited classes, a full project might have multiple inheritances. it therefore allows you to have an interface that will always work.
      this is also importanfor cross-platform development. your interface for the objects will always be the same, no matter how different the uynderlying implementation will be
      (Place cool Signature here)
    • Yes, what yen said.

      It's not neccesarily a normal practice in C++, nor might it be the most efficient way to code in terms of CPU performance.

      All it gains you is the ability to easily replace a class with another class with minimal pain. For example, it could potentially make it easy to replace OpenGL with DirectX (class iGraphicsRenderer).

      Another benefit of interface classes is that it kind of serves the same purpose as Variants in Visual Basic... not as much as void*, but still kind of the same. Instead of a list of variants, your graphics engine would render a list of iDrawable elements in which the drawable elements can range from Sprites to 3D geometry to custom line drawing algorithms.
    • In the case of IAudio and IAudioBuffer - I had secretly implemented a sounds system using Miles, but I couldn't get permission to distribute a "education only" SDK from RADGameTools. They run pretty lean over there and didn't want the support hassle from people without money!

      The system worked fine, and I could switch from DirectSound to Miles with no problem, due to my defiition of an interface class.
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • RE: The "interface" classes.

      You are correct, almost all the member functions described by the abstract classes could just be described in the class itself. However, by doing so, you lose some flexibility.

      For example, lets assume I have 3 object types. The first one draws a circle to the screen at a random position, random size, and random color. The second one does the same, but it draws a rectangle. The third one is the same, except it draws a triangle. Now, I could define each as:

      Source Code

      1. class Circle
      2. {
      3. public:
      4. HRESULT Draw(DeviceObjectType* pDevice);
      5. };
      6. class Square
      7. {
      8. public:
      9. HRESULT Draw(DeviceObjectType* pDevice);
      10. };
      11. class Triangle
      12. {
      13. public:
      14. HRESULT Draw(DeviceObjectType* pDevice);
      15. };
      Display All


      However, doing so would force my drawing code to know which objects I'm drawing:

      Source Code

      1. vector<Circle*> g_Circles;
      2. vector<Squares*> g_Squares;
      3. vector<Triangles*> g_Triangles;
      4. // create shape objects somewhere before calling Render
      5. HRESULT Render(DeviceObjectType* pDevice)
      6. {
      7. for (int i = 0; i < g_Circles.size(); ++i)
      8. {
      9. g_Circles[i]->Draw(pDevice);
      10. }
      11. for (int i = 0; i < g_Squares.size(); ++i)
      12. {
      13. g_Squares[i]->Draw(pDevice);
      14. }
      15. for (int i = 0; i < g_Triangles.size(); ++i)
      16. {
      17. g_Triangles[i]->Draw(pDevice);
      18. }
      19. return S_OK;
      20. }
      Display All


      This isn't so bad, except that you have duplicated functionality 3 times and extending it to have ellipses, octogons, hexagons, etc would require more duplication. Instead, if you define an abstract class:

      Source Code

      1. class Shape
      2. {
      3. public:
      4. virtual HRESULT Draw(DeviceObjectType* pDevice) = 0;
      5. };
      6. class Circle : public Shape
      7. {
      8. public:
      9. virtual HRESULT Draw(DeviceObjectType* pDevice);
      10. };
      11. class Square : public Shape
      12. {
      13. public:
      14. virtual HRESULT Draw(DeviceObjectType* pDevice);
      15. };
      16. class Triangle : public Shape
      17. {
      18. public:
      19. virtual HRESULT Draw(DeviceObjectType* pDevice);
      20. };
      Display All


      Your render function is more clean:

      Source Code

      1. vector<Shape*> g_Shapes;
      2. // create shape objects somewhere before calling Render
      3. HRESULT Render(DeviceObjectType* pDevice)
      4. {
      5. for (int i = 0; i < g_Shapes.size(); ++i)
      6. {
      7. g_Shapes[i]->Draw(pDevice);
      8. }
      9. return S_OK;
      10. }
      Display All


      Another example is used when you want to have the ability to change libraries used by the object. This technique comes in very handy for making cross-platorm games. A cross platform library (Allegro for example) would use abstract classes to take advantage of something on a given system. Allegro uses Direct3D when doing graphics code on Windows and OpenGL for *Nix systems, but the calls you make to the library don't change at all.

      As a side note, this technique is often used in business applications to encapsulate common platform functions (e.g. semaphores, threads, events, etc). If you are new to C++, I would recommend getting "Effective C++" by Scott Meyers ("More Effective C++" and "Effective STL" would also be worth a read). He discusses things in the C++ language item by item (i.e. why you should always declare a virtual destructor for a class that will have classes derived from it). There are several items on abstract base classes.
    • RE: The "interface" classes.

      Good example!
      Mr.Mike
      Author, Programmer, Brewer, Patriot