C++ — Getting to the Good Stuff

We got some input, output and strings going last time. This time we get real intense. Arrays, vectors, maps and “maybe even pointers” apparently are coming up, so keep reading I guess.

ARRAYS

In programming, arrays are pretty cool. They let you store stuff in a list-like format. However, in C++, arrays are decidedly not cool. In my experience, there are very few cases where you’ll need to use an array, most of the time you’ll need something with dynamic capabilities like a vector. In Ruby, arrays can be resized, manipulated and changed at will during runtime. In C/C++, array’s need to be created with a certain size, and they can only contain one type of data. Think of memory as being “locked” during runtime, once you enter that state, you can’t manipulate memory (example: change the size of an array). So if you fill up your array and want to add another item into, that sucks, you can’t. You pretty much need to know beforehand how many items will be going into the array, and set the array to that length at the very beginning. Or just use a vector!

VECTORS

Vectors are basically arrays that have dynamic memory allocation capabilities. They can be resized manually, or just do it automatically when you add something it. What is dynamic memory allocation you ask? Very simply put: there are two types of memory available, the stack, and the heap. The stack must be allocated before runtime, and is essentially locked when the program is executed. The heap is free space that you can access at will to supplement anything that needs more space during runtime. There’s just one catch: the only way to access heap memory is through pointers, which I’ll write about next time.

MAPS

Maps are C++’s version of Ruby hashes. I have never used them so we’ll see how it goes.

IMPLEMENTATION

Let’s start off with arrays. Here I make an array with 5 spots in it, and fill each spot with 10, 20, 30, 40, 50 respectively.

As you can see, it’s fairly straight forward. Just like all other variable declarations, you use a data type and a name, and then you can specify a size, or leave it blank and let C++ figure out how many things you put into the array. Note: this does not let you keep adding things after you you do it initially. C++ will count that initial assignment and make that the max size of the array.

What happens if you try to access numArray[5]? You shouldn’t be able to right? C++ would have protections against that, wouldn’t it? Nope. So be careful with arrays. You need to keep track of your indices and make sure you don’t run off the edge. There’s no safety net here. If you run off the edge, you run into the realm of undefined behavior… a mysterious place wrapped in an enigma. According to this guy:

Undefined behavior comes in many flavors. Here are a few:
1) It will kick your dog.
2) It will reformat your hard drive.
3) It will work without a problem.

I love my dogs, and would never let anyone kick them. However, hypothetically, if you were me and you tried to access numArray[5], this is what you got:

Looks like there was already something there

Printing out the entire numArray would look something like this:

There is no built in print method for arrays, so to print out the entirety of it’s contents we have to iterate over it and print out each piece one at a time.

It’s a little annoying, but it’s just another example of all the unnecessary fluff that high level languages like Ruby carry. Unneeded methods attached to every data structure you can think of that go unused 99% of the time.

C++ for loops are not complicated. There’s an iterator (i) declared with an initial value of zero, because we want to start at the zero’th index of the array. Next is how many times we want the for loop to run, in this case, until (but not including) the iterator has reached the number 5. And the last part is the step that the iterator takes each time, which in this case is a simple incrementor. The ++ operator adds 1 to whatever it is assigned to. Conversely the -- operator subtracts 1. (Hence the name C++ meaning “one level above C” or “C + 1”, or if you’d like — “D”). Anyway, put it together on one line and it looks like this:

// (ITERATOR TYPE & START VALUE; ITERATE UNTIL; STEP AMOUNT)
for (int i = 0; i < 5; i++)

You can also use normal ways of assigning or changing values within the index:

int numArray[5] = { 1, 2, 3, 4, 5 };
numArray[1] = 1000;

That’s pretty much it on arrays.

Vectors are a special class in C++. So as usual we have to do this at the top of our file:

#include <vector>

Vectors offer a lot of functionality. If you’re a person who likes reading docs you can check out the vector docs here.

In order to use vector initialization with a list of numbers like arrays do you need to tell the g++ compiler to use C++ version 11 or 14. To do this just change the command we use like this:

g++ -std=c++11 //for C++11
or
g++ -std=c++14 / g++ -std=c++1y //for C++14

The default for g++ is c++98 I believe, which is almost 20 years old. To shorten this back down to just the g++ command, I made shortcut in my bash profile where if I type in g++ it executes g++ -std=c++14.

If you want to do this just add this to your bash profile:

alias g++='g++ -std=c++11'

Now that we’re all setup, let’s code!

This is how you initialize a vector with no values and no set size. You can push values onto the vector with the push_back() method as seen here. In C++11 the ability to initialize vectors with a list of things was added. The syntax looks like this:

vector<int> numVec {1, 2};

This will give you a vector that contains 1 in the first spot, and 2 in the next spot.

With arrays, you’re stuck with the size you choose at the start, however with vectors, the push_back() method will do some dynamic memory allocation for you and grab new space from the heap and use it to expand the vector. Even during runtime. Awesome. I guess technically if you were really good you could do the same thing. Just find out if you’ve filled up the array minus the last index, then make a pointer to a new array in the heap and keep going. But then where do you stop? How where do you draw the line of doing things manually vs letting the language automate it? Best to just stick with the easier solutions until you absolutely need something manually implemented in my opinion.

Vectors have much more than just push_back(), check the vector docs for more vector methods if you want. Some of the handy ones include

  • push_back()
  • pop_back()
  • insert()
  • front()
  • back()
  • erase()
  • clear()

Lastly, iterating through vectors is pretty much the same, except instead of an int iterator, vectors have a built-in iterator to use.

for(vector<int>::iterator i=numVec.begin(); i != numVec.end(); ++i)
{
   cout << *i << endl;
}

Don’t worry about the *i syntax. What that means is that your iterator i turns into a pointer that points to an index of the vector, and to print out the contents of the index we need to “dereference” the pointer using the * operator. There will be more on pointers next time.

Maps as far as I can tell are just like Ruby hashes. There’s a key and a value, and to access a value in the hash, you use the bracket notation with the key inside.

Like with vectors we need to #include <map> so we can use maps in our program. Initializing maps is just like vectors, except there are two data type arguments used: the key type, and the value type.

map<keytype, valtype> name;
map<string, int> myMap;

Lastly accessing or setting a value in a map is just like in Ruby:

name[key] = value;
myMap["dogs"] = 6;

and to access that value:

name[key]; // => value
myMap["dogs"]; // => 6

Great, you now know the basics of using collections in C++! Next time I’ll give you the low down on the infamous pointer.