Need Help: Trying to understand Abstraction and Inheritance

This is a discussion on Need Help: Trying to understand Abstraction and Inheritance within the C++ Programming forums, part of the General Programming Boards category; To all: I need a little more help in trying to understand Abstraction and Inheritance. I want to have an ...

  1. #1
    Registered User deadpoet's Avatar
    Join Date
    Jan 2004
    Posts
    50

    Question Need Help: Trying to understand Abstraction and Inheritance

    To all:

    I need a little more help in trying to understand Abstraction and Inheritance. I want to have an Animal that is either of class Dog or Cat. Pretend that the Dog and Cat have some common features represented by CommonAnimal. For now we will say that class CommonAnimal will define the color. The specific features for Dog and Cat are contained in their respective speak() methods. When I attempt to compile the source listed below I get the following compiler errors:
    Code:
    Error 596: "animal3.c", line 31 # Within the base class hierarchy of class CommonAnimal, virtual member function "void
        std::Animal::get_color()" is overridden in both class Dog and class Cat, and neither instance hides the other.
            virtual void get_color( void ) = 0;
                         ^^^^^^^^^             
    Error 596: "animal3.c", line 32 # Within the base class hierarchy of class CommonAnimal, virtual member function "void
        std::Animal::speak()" is overridden in both class Dog and class Cat, and neither instance hides the other.
            virtual void speak( void ) = 0;
                         ^^^^^             
    Error 226: "animal3.c", line 70 # No appropriate function found for call of 'operator ='. Last viable candidate was "std::Dog
        &std::Dog::operator =(const std::Dog &)" ["animal3.c", line 36]. Argument of type 'std::Dog *' could not be converted to 'const
        std::Dog &'.
              *ptrDog = new Dog( dog_color );
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 
    Error 226: "animal3.c", line 71 # No appropriate function found for call of 'operator ='. Last viable candidate was "std::Cat
        &std::Cat::operator =(const std::Cat &)" ["animal3.c", line 48]. Argument of type 'std::Cat *' could not be converted to 'const
        std::Cat &'.
              *ptrCat = new Cat( cat_color );
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    What am I doing wrong? I think that I am close but I just cannot figure it out. The source code is listed below.

    Code:
    ///////////////////////////////////////////////////////////////////////
    //                           *************                           //
    //                           **  Animal **                           //
    //                           ** Virtual **                           //
    //                           *************                           //
    //                              ^      ^                             //
    //                             /  IS-A  \                            //
    //                            /          \                           //
    //                    *********         *********                    //
    //                    ** Dog **         ** Cat **                    //
    //                    *********         *********                    //
    //                        ^                 ^                        //
    //                         \      IS-A     /                         //
    //                          \             /                          //
    //                        ******************                         //
    //                        ** CommonAnimal **                         //
    //                        ******************                         //
    ///////////////////////////////////////////////////////////////////////
    
    #include <iostream.h>
    #include <string>
    
    #define DOG 1
    #define CAT 2
    
    namespace std{
    class Animal{
      public:
        Animal(){  cout << "Animal Constructor Called!" << endl; }
        virtual ~Animal() = 0;  // pure virtual function.
        virtual void get_color( void ) = 0;
        virtual void speak( void ) = 0;
      private:
    };  // END OF ANIMAL
    
    class Dog : virtual public Animal {
      public:
        Dog(){}
        Dog( string c ) :color (c){
          cout << "Constructing Dog" << endl;
        }
        void get_color( void ) { cout << "Dog Color: " << color << endl; }
        void speak( void ) { cout << "Dog Says: woof woof" << endl; }
      private:
        string color;
    };  // END OF DOG
    
    class Cat : virtual public Animal {
      public:
        Cat(){}
        Cat( string c ): color (c) {
          cout << "Constructing Cat" << endl;
        }
        void get_color( void ) { cout << "Cat Color: " << color << endl; }
        void speak( void ) { cout << "Cat Says: meow" << endl; }
      private:
        string color;
    };  // END OF CAT
    
    class CommonAnimal : public Dog, public Cat {
      public:
        Dog *ptrDog;
        Cat *ptrCat;
        string dog_color;  // Simulate information known from CommonAnimal.
        string cat_color;  // Simulate information known from CommonAnimal.
        CommonAnimal( ){
          dog_color = "BLACK";
          cat_color = "WHITE";
          *ptrDog = new Dog( dog_color );
          *ptrCat = new Cat( cat_color );
        }
        ~CommonAnimal() {
          cout << "Destroying CommonAnimal" << endl;
          if ( ptrDog != NULL ) { delete ptrDog; }
          if ( ptrCat != NULL ) { delete ptrCat; }
        }
        void get_color( int a ) {
          switch( a ) {
          case DOG:
            ptrDog->get_color();
            break;
          case CAT:
            ptrCat->get_color();
            break;
          }
        }
        void speak( int a ) {
          switch( a ) {
          case DOG:
            ptrDog->speak();
            break;
          case CAT:
            ptrCat->speak();
            break;
          }
        }
      private:
    };  // END COMMONANIMAL
    
    }// END NAME SPACE.
    
    using namespace std;
    int main ( void ) {
      CommonAnimal *ptrCA = new CommonAnimal();
      ptrCA->get_color(1);
      ptrCA->speak(1);
      ptrCA->get_color(2);
      ptrCA->speak(2);
    
      if ( ptrCA != NULL ) { delete ptrCA; }
      return ( EXIT_SUCCESS );
    }  // END OF MAIN
    Thanks in advance to all that reply.

    Sincerely,

    DeadPoet

    Related Posts:Abstract Classes and Object Creation and Access
    Last edited by deadpoet; 03-11-2004 at 12:20 PM.

  2. #2
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,088
    I'm not sure your logic makes sense (and maybe it's just the example you are using). If CommonAnimal is inheriting from both Dog and Cat, then CommonAnimal should be a Dog and it should be a Cat. You say that Dog and Cat have common features represented by CommonAnimal, but those common features are actually represented by the abstract Animal base class. If two classes have common features, they should both derive from the same base class, they should not both be base classes for a common derived class.

    Another thing is, why do you have a pointer to a Dog and a pointer to a Cat in the CommonAnimal class and you have CommonAnimal deriving from Dog and Cat? It should really be one or the other. And unless you are specifically trying to practice with virtual base classes ad multiple inheritance, you should choose containment (the pointer members) instead of inheritance.

    Let's assume that your CommonAnimal is really a home that contains two pets, one dog and one cat. Rename it AnimalHome. Remove the inheritance of Dog and Cat, because the AnimalHome isn't a Dog and a Cat, it contains a Dog and a Cat. That will get rid of the first two compile errors. Then, in your AnimalHome constructor, remove the '*' from the *ptrDog = new Dog( dog_color ); line. In that instance, it is dereferencing the pointer, which is not what you want. Do the same for the Cat line. That will fix your other two errors.

    If you are really trying to use the inheritance hierarchy that you drew, then maybe a better example should be used that actually makes sense for that design.

  3. #3
    Registered User deadpoet's Avatar
    Join Date
    Jan 2004
    Posts
    50
    If it may help clarify the situation the original post which details the actual problem is located at the following C++ Programming URL: Abstract Classes and Object Creation and Access. It is a necessity to have the CommonAnimal class because in reality it will own data structures that are required by Dog and Cat. These would be expensive operations that one does not want to duplicate in other classes. Please follow the link I have provided and you will gain a better understanding of the problem which is placed in to the context of the CInventory class.

    Thanks for your comments, I should have included the URL in the first place. I still need help on this subject. Additionally, I did not notice that I had left the "*". That did fix some of the compile issues. The virtual errors still persist.

    DeadPoet
    Last edited by deadpoet; 03-11-2004 at 12:24 PM.

  4. #4
    Hardware Engineer
    Join Date
    Sep 2001
    Posts
    1,398
    Looking at your diagram, (Sorry, I can't take the time to study your code right now... I'm supposed to be working ), The virtual animal and common animal should be the same thing... the same class. Your drawing makes it look like two different things. The common animal is virtual, because you can't make a common animal "object". The object must be some specific animal... You can create a cat, but you can't create a "common animal".

    So, you probably don't need an Animal class and a CommonAnimal class... What's supposed to be the difference between these two classes? ... Ummm, I think your're on the wrong track with your ComonAnimal class. The virtual base class can have weight, age, color, without regard to whether it's a cat or dog.
    Last edited by DougDbug; 03-11-2004 at 12:45 PM.

  5. #5
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,088
    I remember reading that thread, although it confuses me more as to what you are trying to accomplish . I think you should stick to the example that you are actually working on, because the example you posted here is in my opinion not a good example of using multiple inheritance. It might be a good idea to use the inheritance hierarchy that Prelude suggested for your Inventory/Device problem, but that doesn't mean it translates into a CommonAnimal.

    Also, in your post here, you say that you need a little more help understanding abstraction and inheritance. I think this is true. Even if you require the inheritance hierarchy that you drew in this thread, you should understand why it doesn't apply to Animal example. Just for the fun of it I will keep studying the other thread to see if I can understand what you are looking for and what Prelude was suggesting for your problem.

    By the way, as far as compile errors go, because Dog and Cat both implement their own versions of speak() and get_color(), the compiler cannot figure out which one to call if the user calls speak() from an object of your CommonAnimal class. You must resolve this by overriding the exact function in CommonAnimal. You currently have speak(int) and get_color(int) in CommonAnimal, but those have different parameters than the speak(void) and get_color(void) functions in the base classes. Also, I think you will have to give a definition of your pure virtual destructor, even though it is pure virtual. Destructors are special like that.

  6. #6
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,088
    Reading that thread a few times, I think you need to understand the difference between containment and inheritance, and when to use both. Here is a modified version of your Animal scenario above:
    Code:
    ///////////////////////////////////////////////////////////////////////
    //                        ******************                         //
    //                        ** CommonAnimal **                         //
    //                        ******************                         //
    //                                                                   //
    //                             HAS-MANY                              //
    //                                                                   //
    //                          *************                            //
    //                          **  Animal **                            //
    //                          ** Virtual **                            //
    //                          *************                            //
    //                             ^      ^                              //
    //                            /  IS-A  \                             //
    //                           /          \                            //
    //                   *********         *********                     //
    //                   ** Dog **         ** Cat **                     //
    //                   *********         *********                     //
    ///////////////////////////////////////////////////////////////////////
    
    #include <iostream> // Changed to <iostream>
    #include <vector> // Added for AnimalHome
    #include <string>
    using namespace std;
    
    namespace deadpoet{ // Never add your code to namespace std
    class Animal{
      public:
        Animal(){  cout << "Animal Constructor Called!" << endl; }
        virtual ~Animal() = 0;  // pure virtual function.
        virtual void get_color( void ) = 0;
        virtual void speak( void ) = 0;
      private:
    };  // END OF ANIMAL
    Animal::~Animal() { } // Must define pure virtual destructor
    
    class Dog : virtual public Animal {
      public:
        Dog( string c = "BLACK") :color (c){
          cout << "Constructing Dog" << endl;
        }
        void get_color( void ) { cout << "Dog Color: " << color << endl; }
        void speak( void ) { cout << "Dog Says: woof woof" << endl; }
      private:
        string color;
    };  // END OF DOG
    
    class Cat : virtual public Animal {
      public:
        Cat( string c = "WHITE" ): color (c) {
          cout << "Constructing Cat" << endl;
        }
        void get_color( void ) { cout << "Cat Color: " << color << endl; }
        void speak( void ) { cout << "Cat Says: meow" << endl; }
      private:
        string color;
    };  // END OF CAT
    
    class AnimalHome { // No more inheritance
      public:
        AnimalHome( ) { }
        ~AnimalHome() {
          cout << "Destroying Animals" << endl;
          for (int i = 0; i < myPets.size(); i++)
              delete myPets&#091;i&#093;;
        }
        void add_animal( Animal* ptrAnimal ) {
            if (ptrAnimal != 0)
                myPets.push_back(ptrAnimal);
        }
        void get_color( void ) {
          for (int i = 0; i < myPets.size(); i++)
              myPets&#091;i&#093;->get_color();
        }
        void listen( void ) {
          for (int i = 0; i < myPets.size(); i++)
              myPets&#091;i&#093;->speak();
        }
      private:
        vector<Animal*> myPets; // Store generic Animals 
    };  // END ANIMALHOME
    
    }// END NAME SPACE.
    
    using namespace deadpoet;
    int main ( void ) {
      AnimalHome myHouse;
      myHouse.add_animal(new Dog);
      myHouse.add_animal(new Dog("BROWN"));
      myHouse.add_animal(new Cat);
      myHouse.add_animal(new Dog("GRAY"));
      myHouse.get_color();
      myHouse.listen();
      return ( EXIT_SUCCESS );
    }  // END OF MAIN
    Notice in the listen() and the get_color functions inside of AnimalHome it doesn't matter what type of Animal each entry in the array is, it just matters that they are an Animal of some sort and so they have a color and can speak. Maybe this what you need. A class that holds a bunch of DeviceInventory objects. At first it would only contain two objects - one StorageDeviceInventory and one NetworkDeviceInventory. It would be extensible, though, because you can create new classes that derive from DeviceInventory (Animal in the example above) without changing the InventoryStat class (AnimalHome above).

  7. #7
    Registered User deadpoet's Avatar
    Join Date
    Jan 2004
    Posts
    50
    I am attempting to implement the multiple inheritance that Prelude suggested. I am just trying to keep generate a template that should be easily understood and replicates the problem without making the posting to large. I do however feel that I did screw up the naming. Let me change the names:

    Code:
    ///////////////////////////////////////////////////////////////////////
    //                        *********************                      //
    //                        **  CommonFeatures **                      //
    //                        **     Virtual     **                      //
    //                        *********************                      //
    //                              ^      ^                             //
    //                             /  IS-A  \                            //
    //                            /          \                           //
    //                    *********         *********                    //
    //                    ** Dog **         ** Cat **                    //
    //                    *********         *********                    //
    //                        ^                 ^                        //
    //                         \      IS-A     /                         //
    //                          \             /                          //
    //                        ******************                         //
    //                        **     Animal   **                         //
    //                        ******************                         //
    ///////////////////////////////////////////////////////////////////////
    
    #include <iostream.h>
    #include <string>
    
    #define DOG 1
    #define CAT 2
    
    namespace deadpoet{
    class CommonFeatures{
      public:
        CommonFeatures(){  cout << "CommonFeatures Constructor Called!" << endl; }
        virtual ~CommonFeatures() = 0;  // pure virtual function.
        virtual void get_color( int ) = 0;
        virtual void speak( int ) = 0;
      private:
    };
    CommonFeatures::~CommonFeatures(){}
    
    class Dog : virtual public CommonFeatures {
      public:
        Dog(){}
        Dog( string c ) :color (c){
          cout << "Constructing Dog" << endl;
        }
        void get_color( int c ) { cout << "Dog Color: " << color << endl; }
        void speak( int s ) { cout << "Dog Says: woof woof" << endl; }
      private:
        string color;
    };
    
    class Cat : virtual public CommonFeatures {
      public:
    
        Cat(){}
        Cat( string c ): color (c) {
          cout << "Constructing Cat" << endl;
        }
        void get_color( int c ) { cout << "Cat Color: " << color << endl; }
        void speak( int s ) { cout << "Cat Says: meow" << endl; }
      private:
        string color;
    };
    
    class Animal : public Dog, public Cat {
      public:
        Dog *ptrDog;
        Cat *ptrCat;
        string dog_color;  // Data Structure known from Animal.
        string cat_color;  // Data Structure known from Animal.
        Animal( ){
          cout << "Creating Animal" << endl;
          dog_color = "BLACK";
          cat_color = "WHITE";
          ptrDog = new Dog( dog_color );
          ptrCat = new Cat( cat_color );
        }
        ~Animal() {
          cout << "Destroying Animal" << endl;
          if ( ptrDog != NULL ) { delete ptrDog; }
          if ( ptrCat != NULL ) { delete ptrCat; }
        }
        void get_color( int a ) {
          switch( a ) {
          case DOG:
            ptrDog->get_color( a );
            break;
          case CAT:
            ptrCat->get_color( a );
            break;
          }
        }
        void speak( int a ) {
          switch( a ) {
          case DOG:
            ptrDog->speak( a );
            break;
          case CAT:
            ptrCat->speak( a );
            break;
          }
        }
      private:
    };  // END COMMONANIMAL
    
    
    }// END NAME SPACE.
    
    using namespace deadpoet;
    int main ( void ) {
      Animal *ptrCA = new Animal();
      ptrCA->get_color(1);
      ptrCA->speak(1);
      ptrCA->get_color(2);
      ptrCA->speak(2);
    
      if ( ptrCA != NULL ) { delete ptrCA; }
      return ( EXIT_SUCCESS );
    }
    This code compiles and runs as expected, however I still do not understand why I am forced to define my virtual destructor. Looking back I did have the classes inverted as to the relationships.

    DeadPoet
    Last edited by deadpoet; 03-11-2004 at 01:21 PM.

  8. #8
    Registered User deadpoet's Avatar
    Join Date
    Jan 2004
    Posts
    50
    Let me study your code for a momemt. Great code but some of the meaning has been lost. The AnimalHouse in your example MUST have a data structure that is needed by both Dog and Cat that you must not duplicate. Pretend that this data structure is a structure that was very costly to build like the information collected from lstat on the entire device directory on a UNIX server. One must have this information before building the other objects, such as, Dog and Cat; and one does not want to have to get this information more than once because of the cost.

    You are correct that I do not fully understand all the in's and out's of C++ programming that is why I am raising the question. It is for clairification on a matter that I have some confusion on. You have made some valid changes that I will take into account and fix in this example.

    The fundamentals of the problem have not changed. If I am doing something wrong without a doubt tell me a better way. I do not wish to have an incorrect understanding of a problem as that will lead to an incorrect solution.

    I am still thinking about your source. It has solid meaning but I have to understand the ramifications of your approach.

    DeadPoet
    Last edited by deadpoet; 03-11-2004 at 01:29 PM.

  9. #9
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,088
    Ok, so you have a data structure that is very big and must only be filled once and there must only be one copy of this data structure around so as to save space and time.

    Then, you have other objects that require information in that data structure before they can be constructed. There are at least two different types of these objects. Since both need to look up information in the data structure, they both have some common features.

    Is this correct?

    In my example, the AnimalHouse must not know about Dogs, Cats, or any other specific Animal. It can only know about the Animal base class. Also, the Dog, Cat and Animal classes must not know about the AnimalHouse, why should a pet care what house they are in. The key is to only build dependencies between objects when absolutely necessary. I will think a bit more on a design for the data structure problem.

  10. #10
    Registered User deadpoet's Avatar
    Join Date
    Jan 2004
    Posts
    50

    Wink

    Yes. You are right that the pet does not care about the house in every case, but if the pet were a fish and its house was a fish bowl then does the fish bowl have water. Water acting like a really big data structure that is a property of the House(container).

    The Dog or Cat might care if the house has heat for the winter where heating is a property of the House that must be told to the Pet before the pet will come and live with you.

    Disclaimer:
    Most pets really do not care about the heating just as long as you feed them...I think.

    DeadPoet

  11. #11
    Registered User jlou's Avatar
    Join Date
    Jul 2003
    Posts
    1,088
    Here's another go:
    Code:
    ////////////////////////////////////////////////////////////////////////////
    //                                                                        //
    //                            **********************                      //
    //                            ** InventoryManager **                      //
    //                            **********************                      //
    //                                  /         \                           //
    //                  CONTAINS MANY  /           \  CONTAINS ONE            //
    //                                /             \                         //
    //                               /               \                        //
    //                              /                 \                       //
    //                             /                   \                      //
    //           *********************                  \                     //
    //           ** DeviceInventory **  REFERENCES  **********************    //
    //           **   (Abstract)    ** -----------> ** BigDataStructure **    //
    //           *********************              **********************    //
    //                      ^      ^                                          //
    //                     /  IS-A  \                                         //
    //                    /          \                                        //
    // **********************      **********************                     //
    // ** StorageInventory **      ** NetworkInventory **                     //
    // **********************      **********************                     //
    //                                                                        //
    ////////////////////////////////////////////////////////////////////////////
    
    #include <iostream>
    #include <vector>
    #include <string>
    using namespace std;
    
    namespace deadpoet{
    
    class BigDataStructure { // Takes up lots of resources.
      public:
        BigDataStructure() { } // Does a bunch of stuff like call lstat.
    };
    
    class DeviceInventory{
      public:
        DeviceInventory(BigDataStructure& bds) : commonDataStructure(bds) { }
        virtual ~DeviceInventory() = 0;  // pure virtual function.
        // ... Other Public Interface
      protected:
        // ... Interface to commonDataStructure
      private:
        BigDataStructure& commonDataStructure;
    };  // END OF DEVICEINVENTORY
    DeviceInventory::~DeviceInventory() { } // Must define pure virtual destructor
    
    class StorageInventory : public DeviceInventory {
      public:
        StorageInventory(BigDataStructure& bds) : DeviceInventory(bds) { }
        // ... Add implementation and virtual function overrides.
    };  // END OF STORAGEINVENTORY
    
    class NetworkInventory : public DeviceInventory {
      public:
        NetworkInventory(BigDataStructure& bds) : DeviceInventory(bds) { }
        // ... Add implementation and virtual function overrides.
    };  // END OF NETWORKINVENTORY
    
    class InventoryManager {
      public:
        InventoryManager() { } // Silently calls BigDataStructure constructor.
        ~InventoryManager() {
          for (int i = 0; i < deviceInventories.size(); i++)
            delete deviceInventories&#091;i&#093;;
        }
        void addStorageInventory() {
          deviceInventories.push_back(new StorageInventory(bigDataStructure));
        }
        void addNetworkInventory() {
          deviceInventories.push_back(new NetworkInventory(bigDataStructure));
        }
      private:
        BigDataStructure bigDataStructure;
        vector<DeviceInventory*> deviceInventories;
    };  // END INVENTORYMANAGER
    
    }// END NAME SPACE.
    
    using namespace deadpoet;
    int main ( void ) {
      InventoryManager inventoryMgr;
      inventoryMgr.addStorageInventory();
      inventoryMgr.addNetworkInventory();
      // ... Lots of stuff with the network and storage inventories.
      return ( EXIT_SUCCESS );
    }  // END OF MAIN
    In this example, the InventoryManager contains the StorageInventory and the NetworkInventory. This was one of the suggestions that Prelude made instead of the multiple inheritance. I think this is the better way to go. I have the contained objects in a vector of pointers to the base class (DeviceInventory), but that is not the only way to do it. They could also just be members of type StorageInventory and NetworkInventory.

    In this case, the InventoryManager does know about the classes derived from DeviceInventory (the base class), because it must create these classes itself. This is different from my earlier version of AnimalHome which didn't know about the derived classes.

    The key is that the InventoryManager manages a single BigDataStructure. There is only one BigDataStructure per InventoryManager, and I assume your code would only require one InventoryManager. This keeps your requirement that the big data structure be created only once and have only one copy exist.

    Because the DeviceInventory objects must use the BigDataStructure when they are created, I have the InventoryManager pass a reference to that data structure to the constructors of those objects. This way, the BigDataStructure itself is never copied or reloaded, but any new DeviceInventory objects have access to it.

    I'm sure there are ways to improve this design, but does this come closer to your goal of managing a single big data structure that must be used by each DeviceInventory?

  12. #12
    Registered User deadpoet's Avatar
    Join Date
    Jan 2004
    Posts
    50
    Dude, you are impressive. Let me digest the information.

    Thanks,

    DeadPoet

    Update 1:
    I think that I was not looking at the problem in that manner and I did not fully grasp what Prelude was conveying.

    Update 2:
    The more I look at your code, I can honestly say I would not have come to your solution in a timely manner. The solution that you and Prelude have is much better than mine. Thanks a million.

    DeadPoet 5:54 PM
    Last edited by deadpoet; 03-11-2004 at 06:19 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 16
    Last Post: 06-08-2009, 03:03 PM
  2. Multiple Inheritance - Size of Classes?
    By Zeusbwr in forum C++ Programming
    Replies: 10
    Last Post: 11-26-2004, 08:04 AM
  3. inheritance and performance
    By kuhnmi in forum C++ Programming
    Replies: 5
    Last Post: 08-04-2004, 12:46 PM
  4. Inheritance and Polymorphism
    By bench386 in forum C++ Programming
    Replies: 2
    Last Post: 03-18-2004, 09:19 PM
  5. Inheritance vs Composition
    By Panopticon in forum C++ Programming
    Replies: 11
    Last Post: 01-20-2003, 03:41 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21