Why you should avoid arrays using CPP

From Compsci.ca Wiki

(Difference between revisions)
Jump to: navigation, search
m
 
Line 2: Line 2:
Yes, in Turing, and other languages, where arrays are either well-implemented, or there's simply no decent alternative.
Yes, in Turing, and other languages, where arrays are either well-implemented, or there's simply no decent alternative.
 +
==C++ has a better alternative==
==C++ has a better alternative==

Latest revision as of 14:49, 11 June 2007

Contents

But... don't you tell us to use arrays?

Yes, in Turing, and other languages, where arrays are either well-implemented, or there's simply no decent alternative.


C++ has a better alternative

The vector class in the Standard Template Library provides a more flexible, powerful alternative to using arrays.

Whoa, whoa, whoa... what's the Standard Template Library?

The STL is a collection of classes and functions which revolve around the C++ concept of templates, whereby we can create types and functions which operate on many types of data, without immediately specifying those types, or writing separate classes or functions for every type.

Don't worry, GCC includes an implementation of the STL, as do most other compilers these days. Visual C++ 6.0, it should be noted, has an implementation of it, but that implementation is quite bad. If you use this compiler, you may be led to believe the STL is a bad thing, when it's really the compiler at fault.

A simple template class

Now, we could write the following trivial code: Now, we could write the following trivial code:

#include <string>
#include <iostream>

class int_math
{
   private:
      int original;
   public:
      int_math(int original_) : original(original_) { }
      int add(int other) const { return original + other; }
      int value() const { return original; }
};

class double_math
{
   private:
      double original;
   public:
      double_math(double original_) : original(original_) { }
      double add(double other) const { return original + other; }
      double value() const { return original; }
};

int main()
{
   int_math a(8);
   double_math b(7.0);

   std::cout << a.add(5)
             << std::endl
             << b.add(41.3)
             << std::endl;

   return 0;
}

But, we should notice that we're duplicating a lot of code and this is a bad thing in programming, when it can be avoided. Fortunately C++ gives us a way to avoid code duplication in this instance.

#include <string>
#include <iostream>

template <typename _t>
class math
{
   private:
      _t original;
   public:
      math(_t original_) : original(original_) { }
      _t add(_t other) const { return original + other; }
      _t value() const { return original; }
};

int main()
{
   math<int> a(8);
   math<double> b(7.0);

   std::cout << a.add(5)
             << std::endl
             << b.add(41.3)
             << std::endl;

   return 0;
};

Back to why arrays are bad

So, why are arrays bad, you ask. Well, reason number one is that they do not keep track of their own size. We have to do it manually, and that's just asking for trouble.

Consider an example where we want to ask for series of grades and find the average of them. We might get as many as ten, but we might get less, so we have to keep track of the number input.

#include <iostream>

int main()
{
   int grades[10];
   int num_grades = 0;
   char answer = 'y';

   while (answer == 'y')
   {
      std::cin >> grades[num_grades];
      num_grades++;

      do
      {
         std::cout << "Enter another? ";
         std::cin >> answer;
      } while (!(answer == 'y' || answer == 'n'));
   }

   int sum = 0;

   for (int i = 0; int i < num_grades; i++)
   {
      sum += grades[i];
   }

   std::cout << "Average grade: " 
             << (sum / num_grades)
             << std::endl;

   return 0;
}

Now, let's look at the same example, using std::vector.

#include <iostream>
#include <vector>

int main()
{
   std::vector<int> grades;
   char answer = 'y';

   while (answer == 'y')
   {
      int temp_grade;
      std::cin >> temp_grade;
      grades.push_back(temp_grade);

      do
      {
         std::cout << "Enter another? ";
         std::cin >> answer;
      } while (!(answer == 'y' || answer == 'n'));
   }

   int sum = 0;

   for (std::vector<int>::iterator i(grades.begin()); i != grades.end(); i++)
   {
      sum += *i;
   }

   std::cout << "Average grade: "
             << (sum / grades.size())
             << std::endl;
   return 0;
}

The important bits are:

grades.push_back(temp_grade)

And:

Code: grades.size()


The first adds a new element (in this case a grade) onto the end of the vector. The second line grabs the size of the vector. The std::vector is just a class, so getting the size of one is as simple as calling a member function. Smile

Since std::vector is a class...

Of course, now that we've said that std::vector is just a class, this means we could implement a lot of features by simply subclassing std::vector.

#include <iostream>
#include <vector>

class grades_collection : public std::vector<int>
{
   public:
      int sum()
      {
       int sum = 0;

         for (iterator i(begin()); i != end(); i++)
         {
           sum += *i;
        }

        return sum;
      }

      int average()
      {
       return sum() / size();
     }
};

int main()
{
   grades_collection grades;
   char answer = 'y';

   while (answer == 'y')
   {
      int temp_grade;
      std::cin >> temp_grade;
      grades.push_back(temp_grade);

      do
      {
         std::cout << "Enter another? ";
         std::cin >> answer;
      } while (!(answer == 'y' || answer == 'n'));
   }

   std::cout << "Average grade: "
             << grades.average()
             << std::endl;
   return 0;
}

Make it even simpler

We could simplify this even further by declaring a friend function which gets input and puts it into the vector.

#include <iostream>
#include <vector>

class grades_collection : public std::vector<int>
{
   public:
      int sum()
      {
       int sum = 0;

         for (iterator i(begin()); i != end(); i++)
         {
           sum += *i;
        }

        return sum;
      }

      int average()
      {
       return sum() / size();
     }

     friend std::istream& operator>>(std::istream& in, grades_collection& g);
};

std::istream& operator>>(std::istream& in, grades_collection& g)
{
   int temp_grade;
   in >> temp_grade;
   g.push_back(temp_grade);
   return in;
}

int main()
{
   grades_collection grades;
   char answer = 'y';

   while (answer == 'y')
   {
      std::cin >> grades;

      do
      {
         std::cout << "Enter another? ";
         std::cin >> answer;
      } while (!(answer == 'y' || answer == 'n'));
   }

   std::cout << "Average grade: "
             << grades.average()
             << std::endl;
   return 0;
}

In Summary

There's more to come, but for now, we've seen that vectors offer a distinct advantage by knowing their own size.

We've also seen that their object-oriented nature makes it trivial to extend them, and this ability can make dealing with them much simpler.

Discussion

To discuss this tutorial visit here.

Credits

Tutorial written by wtd, moved to wiki by Cornflake

Personal tools