Thread: Inheritance and Arrays

  1. #1
    Registered User codegirl's Avatar
    Join Date
    Jun 2003
    Posts
    76

    Talking Inheritance and Arrays

    I have an abstract base class, call it ABC. It has two derived classes, say Derived1 and Derived2. In another class, I need an array of objects of either Derived1 or Derived2. The problem is I don't know if the array elements should be of type Derived1 or Derived2 until run time. (I have a variable called "mode", and the value of mode determines if the array should contain Derived1 or Derived2 objects.) I can't seem to create an array of ABC pointers since you can't create instances of an abstract class. So how can I create this array?

    Thank you!!
    Last edited by codegirl; 06-05-2003 at 01:27 PM.

  2. #2
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    can you do this:

    class C and D derived from base class B which is derived from abstract class A. Then use array of pointers to class B.

  3. #3
    Registered User codegirl's Avatar
    Join Date
    Jun 2003
    Posts
    76
    I suppose I could, or I could just make A not an abstract class and derive C and D directly from A. I just liked the idea of forcing derived classes to include certain functions... oh well, I suppose the compiler will catch it if another programmer tries to call a non-existant function!

  4. #4
    Toaster Zach L.'s Avatar
    Join Date
    Aug 2001
    Posts
    2,686
    You can make an array of ABC*, just make sure never to instantiate them as type ABC (i.e. ABC* y = new ABC; is an error). Instead, instantiate them as the derived type (ABC* y = new Derived1).

    Cheers
    The word rap as it applies to music is the result of a peculiar phonological rule which has stripped the word of its initial voiceless velar stop.

  5. #5
    Registered User codegirl's Avatar
    Join Date
    Jun 2003
    Posts
    76
    But I also don't know the size of the array until runtime. C++ doesn't seem to like an array declaration like:

    ABC* myarray[size];

    when size is not a constant. The only way I know how to create such an array is with new.

    For that matter, even when I decided to make ABC not abstract and just a regular parent class, I can't seem to get the array to work. All of its members are getting cast as type ABC, not as the derived type! (I can show you some code but I've tried quite a few things, so I don't know how much it will help!)

    Yikes, this is getting messy!

  6. #6
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    here's a reasonable discussion of pointers to base classes:

    http://www.stud.fim.ntnu.no/~oystesk/CPP/htm/ch13.htm

    to use an array of base class pointers of unkown size I suspect you would need to do something like this:

    ABC * ptrArray = new ABC *[size];

    where size is a variable. Now ptrArray will act as an array of pointers to type ABC which is the base class for Derived1 and Derived2. No instance of ABC ever need be declared, so ABC can be abstract if necessary. See above reference for discussion.

  7. #7
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    C++ doesn't seem to like an array declaration like:

    ABC* myarray[size];

    when size is not a constant.
    An array size has to be known at runtime, so the compiler can set aside the memory. Otherwise, you have to dynamically allocate the memory.

    Applying what Zack L. said:

    int size = 0;
    int mode = 0;
    cin>>size>>mode;

    if(mode==10)
    ABC* y = new Derived1[size];
    else
    ABC* y = new Derived2[size];

  8. #8
    Registered User codegirl's Avatar
    Join Date
    Jun 2003
    Posts
    76
    yStud --

    When I tried

    Code:
    if (mode == 1)
      ABC* ptrArray = new Derived1[size];
    else
      ABC* ptrArray = new Derived2[size];
    It's still treating the elements of ptrArray as ABC pointers, not as Derived1/Derived2 pointers.


    elad --

    When I tried

    ABC * ptrArray = new ABC *[size];

    I got an error that I was trying to do an illegal implicit cast from ABC* to ABC**. So I changed it to

    ABC ** ptrArray = new ABC *[size];

    and later I have the code:

    Code:
    for (int i=0; i<size; i++) {
    		if (mode == 1)
        ptrArray[i] = new Derived1;
    		else
        ptrArray[i] = new Derived2;
    }
    and it still treated the members of ptrArray as ABC type, not derived types. But thanks for that link -- I will go look through that and maybe I can find something to help me out!
    My programs don't have bugs, they just develop random features.

  9. #9
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    "and it still treated the members of ptrArray as ABC type, not derived types."

    That's the whole point of polymorphism: you can use pointers to base class objects to point to derived class objects. If you don't need polymorphic behaviour, why not just dynamically create an array of pointers to Derived1 or Derived2 objects?
    -----

    ABC ** ptrArray = new ABC *[size];

    If you have an array of int's, then a pointer to the array points to the first element which is an int, so the type of the pointer is pointer to int, or int*. If the array is an array of int pointers, then a pointer to that array points to the first element which is of type int*. So, the pointer to that array must be type pointer to int*, or int**.
    Last edited by 7stud; 06-05-2003 at 12:30 PM.

  10. #10
    Registered User
    Join Date
    Jan 2003
    Posts
    311
    use std::vector<ABC *> v;

    to add a Derived1 v.push_back(new Derived1(/*args if needed*/));

    any calls on methods of ABC will call the Derived method iff those methods are virtual.

    If you need to use something that's not in the ABC you need to use dynamic_cast<Derived1 *>(v[3]); This will give you a pointer iff v[3] actually points to a Dervived1.

    Naturally, you need to rember to delete each element.

  11. #11
    Registered User codegirl's Avatar
    Join Date
    Jun 2003
    Posts
    76
    7stud --

    "and it still treated the members of ptrArray as ABC type, not derived types."

    That's the whole point of polymorphism: you can use pointers to base class objects to point to derived class objects. If you don't need polymorphic behaviour, why not just dynamically create an array of pointers to Derived1 or Derived2 objects?
    Hmm, then maybe I don't understand polymorphism like I thought I did (which is always a possibility!). I thought the purpose of arrays of base class pointers is so they can store objects of the derived types, and the functions of the derived types will be called -- the example I always hear of is an array of screen objects, each with its own draw method.

    Granted, I don't need to mix the types in my array. But I use the array so many times, it seemed silly to create two arrays of the different derived types and each time I need to use them, check the mode to see which array to use. Or alternatively, have two functions with the exact same code except for the array types, because these two derived classes have the same functions. In my particular instance, the derived types have functions that the base class does not. The way I know the pointers in the array are being treated as base classes is because the compiler is saying those functions are undefined.

    So perhaps I should ask a new question -- is there a way to dynamically set the type of the array? I've tried code like

    Code:
    if (mode == 1)
      Derived1 ptrArray = new Derived1[size];
    else
      Derived2 ptrArray = new Derived2[size];
    but after the if statement I get errors saying ptrArray wasn't initallized. (I don't remember the exact code I tried, I deleted it a while ago.) Does that make any sense or am I just making things more confusing?

    Oops I just realized why that if statement didn't work -- the array only exists in the scope of the if! Still, is there a way to do what I'm asking? Or am I just going to have a bunch of duplicate code or if statements to check the mode?


    grib --

    Your post just appeared when I hit reply. While your code does look promising (I do seem to remember seeing dynamic_cast somewhere), I'd still have the same problem I just described -- I'd have to keep checking the mode to see what type I need to cast! What I really need to know is, is there a way to avoid this?

    Thanks for all the help you guys have given me!
    My programs don't have bugs, they just develop random features.

  12. #12
    Toaster Zach L.'s Avatar
    Join Date
    Aug 2001
    Posts
    2,686
    The derived class' function will get called if the base class' function was declared as 'virtual'.

    There is no real good way to dynamically set the array type. You could use void* to get generic pointers and then dynamic_cast them each time you make a function call, but that is really a type-unsafe and scaled down version of what you were doing.

    Look some more into virtual functions, and RTTI, and I think you'll find what you're looking for.

    Cheers
    The word rap as it applies to music is the result of a peculiar phonological rule which has stripped the word of its initial voiceless velar stop.

  13. #13
    Registered User codegirl's Avatar
    Join Date
    Jun 2003
    Posts
    76
    Hey! I think I fixed it!

    Grib's comments got me to thinking... since the functions I needed were not in the base class, I just put them in the base class, made them virtual, and had them print an error message to the screen. Since the code compiled and no error messages were printed, I think it's working! If you're interested here's how I finally created that array:

    Code:
    ABC ** ptrArray = new ABC* [size];
    for (int i = 0; i < size; i++) {
    		if (mode == 1)
    			ptrArray[i] = new Derived1;
    		else
    			ptrArray[i] = new Derived2;
    }
    Of course now my program's crashing inside one of the derived classes -- you fix one error just to get a new one! Oh well, at least one class seems to be working Thanks again for all your help!
    My programs don't have bugs, they just develop random features.

  14. #14
    Registered User
    Join Date
    Mar 2002
    Posts
    1,595
    Let's see if I can work through this correctly. NOT compiled, so may well have errors in it.
    Code:
    1:     //modified from TYC++ 3rd edition  example 13.9
    2:
    3:     #include <iostream>
    4:
    5:     using namespace std;
    6:
    7:     class Shape
    8:     {
    9:     public:
    10:       Shape(){}
    11:       ~Shape(){}
    12:       
    13:       
    14:       virtual void Draw() = 0;
    15:    private:
    16:    };
    17:     //yes, you can define pure virtual functions
    18:     void Shape::Draw()
    19:    {
    20:       cout << "Base class method called but not base class object initialized\n";
    21:    }
    22:
    23:    class Circle : public Shape
    24:    {
    25:    public:
    26:          Circle(int radius):itsRadius(radius){}
    27:          ~Circle(){}
    28:          
    29:          
    30:          void Draw();
    31:    private:
    32:       int itsRadius;
    33:       int itsCircumference;
    34:    };
    35:
    36:    void Circle::Draw()
    37:    {
    38:       cout << "Circle drawing routine here!\n";
    39:       Shape::Draw();
    40:    }
    41:
    42: 
    43:    class Rectangle : public Shape
    44:    {
    45:    public:
    46:          Rectangle(int len, int width):
    47:             itsLength(len), itsWidth(width){}
    48:          ~Rectangle(){}
    49:          
    50:          
    51:          
    52:          
    53:          void Draw();
    54:    private:
    55:       int itsWidth;
    56:       int itsLength;
    57:    };
    58:
    59:    void Rectangle::Draw()
    60:    {
    61:       for (int i = 0; i<itsLength; i++)
    62:       {
    63:          for (int j = 0; j<itsWidth; j++)
    64:             cout << "x ";
    65:
    66:          cout << "\n";
    67:       }
    68:       
    69:    }
    70:
    71:
    72:    class Square : public Rectangle
    73:    {
    74:    public:
    75:          Square(int len);
    76:          
    77:          ~Square(){}
    78:    
    79:    };
    80: 
    81:    Square::Square(int len):
    82:       Rectangle(len,len)
    83:    {}
    84:
    85:    
    86:       
    87:
    88:    
    89:       
    90:     
    91:  
    92:
    93:    int main()
    94:    {
    95:       int choice;
    96:       bool fQuit = false;
    97:       Shape * sp;
    98:        
    99:       while (1)
    100:      {
    101:         cout << "(1)Circle (2)Rectangle (3)Square (0)Quit: ";
    102:         cin >> choice;
    103:
    104:         switch (choice)
    105:         {
    106:            case 1: sp = new Circle(5);
    107:            break;
    108:            case 2: sp = new Rectangle(4,6);
    109:            break;
    110:            case 3: sp = new Square (5);
    111:            break;
    112:            default: fQuit = true;
    113:            break;
    114:         }
    115:         if (fQuit)
    116:            break;
    117:
    118:         sp->Draw();
    119:         cout << "\n";
    120:      }
                 //the above uses a pointer to an abstract base class to call methods for objects of classes derived from that abstract base class.  In the case of Circle, it also demonstrates that you can call virtual base class methods, even pure virtual base class methods, if you want/need to.
                //now I'll try to create an array of base class pointers to store data, then use the data, then delete the memory
               
               Shape * arrayShapePtrs[4];
               for(int i = 0; i < 4; i++)
               {
                         cout << "(1)Circle (2)Rectangle (3)Square";
                        cin >> choice;
     
                        switch (choice)
                        {
                             case 1: 
                                 sp = new Circle(5);
                                 break;
                             case 2: sp = new Rectangle(4,6);
                                 break;
                             case 3: sp = new Square (5);
                                 break;
                             default: 
                                   break;
                        }
                        arrayShapePtrs[i] = sp;
                }
    
                for(i = 0; i < 4; i++)
                   arrayShapePtrs[i]->Draw();
              
                for(i = 0; i < 4; i++)
                   delete arrayShapePtrs[i];
                
           //and now I'll try to use user input to determine size of array of pointers to Shape, use the pointers and delete the memory
           int size;
           cout << "input number of shapes to use" << endl;
           cin >> size;
    
           //an array of pointers on the heap
           Shape ** pointers;
           pointers = new Shape * [size];
    
           //fill up the array
           for(i = 0; i < size; ++i)
           {
                  cout << "(1)Circle (2)Rectangle (3)Square";
                  cin >> choice;
     
                        switch (choice)
                        {
                             case 1: 
                                 sp = new Circle(5);
                                 break;
                             case 2: sp = new Rectangle(4,6);
                                 break;
                             case 3: sp = new Square (5);
                                 break;
                             default: 
                                   break;
                        }
                    pointers[i] = sp;
              }
             
               //use the array of pointers
               for(i = 0; i < 4; i++)
                   pointers[i]->Draw();
              
                //delete memory for the individual pointers stored in the array
                for(i = 0; i < 4; i++)
                   delete pointers[i]; 
            
                //delete memory for the array itself
                delete [] pointers;
           
           //and if I didn't make any mistakes, I'm done    
           return 0;
    }
    Last edited by elad; 06-06-2003 at 01:33 PM.

  15. #15
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    "Hmm, then maybe I don't understand polymorphism like I thought I did (which is always a possibility!)."

    It sounds like you understand polymorphism just fine.

    When you said:

    "It's still treating the elements of ptrArray as ABC pointers, not as Derived1/Derived2 pointers."

    I guess you meant you weren't getting the polymorphic behavior you wanted.

    "since the functions I needed were not in the base class, I just put them in the base class, made them virtual, and had them print an error message to the screen. "

    Yes, if you want polymorphic behavior, the function has to be in the base class and it has to be declared virtual. Declaring the function virtual means, you want to have what's called "dynamic binding": the function call is determined at run time by what type of object the pointer points to. If the function isn't declared virtual, you get a static call to the base class function: the function call is determined at compile time based on the type of the pointer--so no polymorphism. If you don't declare the function at all in the base class, then you're statically trying to call a non existent base class function, so I would think the compiler would go looking for the function at compile time, fail to find it, and give you an error.
    Last edited by 7stud; 06-05-2003 at 05:39 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. struct array's inheritance
    By emorrp1 in forum C Programming
    Replies: 8
    Last Post: 06-27-2008, 05:54 AM
  2. Object Arrays and Inheritance
    By AceHigh in forum C++ Programming
    Replies: 7
    Last Post: 07-28-2002, 04:08 AM