1

So in my code I would like to make an array of classes. This is because I want to change the order with which they do things. However, I need the classes in this array to have a function that is the same name but will do different things, depending on the type of class that is called.

For example, let's say I make an array of Cars. Every car has a function called drive() that determines the speed with which it drives. I make an array of Cars "CarArray[]" In it, I put two types of classes, Hondas and Fords. Both Hondas and toyotas inherit from the base class of Cars. They both have a function called drive(). If I call CarrArray[].drive() and I get a honda, it should out put a different value than if the space were occupied by a Ford.

So, for example, let's say I make a Honda drive at 10 mph and a Ford drive at 30 mph.

My Array looks like this:

CarArray [0] // Honda

CarArray [1] // Ford

CarArray [2] //Honda

If I call CarArray[0].drive() I should get 10 mph

But if I call CarArray[1].drive I should get 30 mph.

Since it is an array. I can manipulate the classes and switch them around so I get different values.

In my program I want to do more than just print out values but I hope you get the idea.

Here is my test code.

        #include <iostream>

using namespace std;


class BaseClass{
public:

 virtual void OutPut();

protected:

        int BaseData;
};
void BaseClass::OutPut(){
    cout<< "BaseClass printed something"<<endl;
}

class BaseA: public BaseClass{
public:

 virtual void OutPut();

protected:

        int BaseData;
};

void BaseA::OutPut(){
    cout<< "BaseA printed something"<<endl;
}

 class BaseB: public BaseClass{
public:

 virtual void OutPut();

protected:

        int BaseData;
};

void BaseB::OutPut(){
    cout<< "BaseB printed something"<<endl;
}
int main()
{


BaseA Class1;
BaseB Class2;

BaseClass BaseArray[2];

BaseArray[0] = Class1;
BaseArray[1] = Class2;

BaseArray[0].OutPut();
BaseArray[1].OutPut();


return 0;
}
Elliott Miller
  • 355
  • 2
  • 5
  • 13
  • 1
    You can't make an array of base class objects and then assign a different type - even a derived type. But you can make an array of pointers to base class objects and assign them to derived classes. `BaseClass* BaseArray[2]; BaseArray[0] = new BaseA; BaseArray[0]->OutPut();` – Jerry Jeremiah Feb 17 '14 at 04:19

3 Answers3

1

Use a container - a vector should suffice:

#include <vector>

// <- your other code

std::vector<BaseClass*> myArray;
myArray.push_back(new BaseA);
myArray.push_back(new BaseB);

for(unsigned int i=0;i<myArray.size();i++){
   myArray[i]->OutPut();
}

If you really want to stick with an array, declare it like this:

Base* array[SIZE];

or

Base** array = new Base*[SIZE];

And use it like the vector above:

for(unsigned int i=0;i<SIZE;i++){
   array[i]->OutPut();
}

In both cases - don't forget to call delete on every element before you finish your program, if you allocate objects using new. Of course you can also add objects by address in both cases:

BaseB someObj;
myArray.push_back(&someObj);
Paweł Stawarz
  • 3,952
  • 2
  • 17
  • 26
  • Is there a way of doing this without vectors or do I have no choice? Do vectors give me a resource penalty? – Elliott Miller Feb 17 '14 at 04:19
  • @ElliottMiller there is, I edited the post. Vectors are a part of the standard, using them would make your program more flexible, since they can change size, and arrays can't. On DevC++ 4.9.9.2, `sizeof(vector)` return `12`. + of course the functions to handle the vector. It's not a big penalty for what it offers. – Paweł Stawarz Feb 17 '14 at 04:26
  • Thank you! Btw I've learned to avoid devC++ as it is not actively updated. I use code::blocks instead. – Elliott Miller Feb 17 '14 at 09:05
  • I use devC++ only for fast testing, since it doesn't require creating a project to compile simple code :) If you would want to return to Dev, there's wxDev which is updated AFAIK. – Paweł Stawarz Feb 18 '14 at 00:02
1

What you're asking for is called Polymorphism. Polymorphism lets you take a pointer or reference to a base class but access methods on the derived class without knowing the exact implementation.

class Base {
    public:
        virtual void doSomething() { std::cout << "Base" << std::endl; }
};

class DerivedA : public Base {
    public:
        // This doesn't have to be virtual but it helps if you want to inherit
        // again.
        virtual void doSomething() { std::cout << "DerivedA" << std::endl; }
};

class DerivedB : public Base {
    public:
        // Same as above
        virtual void doSomething() { std::cout << "DerivedB" << std::endl; }
};

Given the above classes if you have a pointer to base and call doSomething then it will actually call the child method. For example:

Base* base = new DerivedA;
base->doSomething(); // Prints DerivedA

Now we can move to arrays. In this case you want an array of pointers to your base class like so:

Base* things[2];
things[0] = new Base;
things[1] = new DerivedA;
things[2] = new DerivedB;

// Prints Base, DerivedA then DerivedB.
for(int i = 0; i < 2; ++i) {
    std::cout << things[i] << std::endl;
}

Typically in C++ we try to avoid arrays for general collections because they avoid a lot of nice features that we enjoy in modern day programming languages. That's why a std::vector is often recommended. A std::vector is almost as fast as a raw array but automatically scales to the amount of items you add. It's important to understand the performance implications of different collections but when you're just starting out a vector is a fine starting point. The above written in vector syntax looks like this:

std::vector<Base*> things;
things.push_back(new Base);
things.push_back(new DerivedA);
things.push_back(new DerivedB);

// For other ways to iterate over a container see:
// http://stackoverflow.com/a/14351046
for(auto it = things.begin(); it != things.end(); ++it) {
    std::cout << (*it)->doSomething() << std::endl;
}

Finally when you're working with pointers or if you ever see the new keyword you need to make sure to call delete on the pointers when you're done otherwise your program will memory leak. In the vector's case you need to loop over the pointers and delete each one. For example:

std::vector<Base*> things;
things.push_back(new Base);
things.push_back(new DerivedA);
things.push_back(new DerivedB);

// For other ways to iterate over a container see:
// http://stackoverflow.com/a/14351046
for(auto it = things.begin(); it != things.end(); ++it) {
    std::cout << (*it)->doSomething() << std::endl;
}

// Clean up after ourselves
for(auto it = things.begin(); it != things.end(); ++it) {
    delete (*it);
}

There are other approaches that will automatically clean up your memory but that's outside the scope of this answer.

Jake Woods
  • 1,808
  • 15
  • 20
1

This is where we use runtime polymorphism. I have elaborated your Car example. May be this helps.

class Car
{
public:
   virtual void drive() = 0; // this is a pure virtual function and this is how you make a class abstract.
};

class Honda : public Car
{
public:
   void drive()
   {
       std::cout<<"Honda: Driving at: 10mph"<<std::endl;
   }
};

class Ford : public Car
{
public:
   void drive()
   {
      std::cout<<"Ford: Driving at: 30mph"<<std::endl;
   }
};

void main()
{
    Car* cars[2] = {0};
    cars[0] = new Honda();
    cars[1] = new Ford();

    // Now, cars[0].drive() should give you "..10mph".
    cars[0].drive();
    // Now, cars[1].drive() should give you "..30mph".
    cars[1].drive();

    // Don't forget to free memory
    delete[] cars; // Ideally you should have a virtual destructor in Car class and override it in derived. Since our example is simple I omitted it.
}