It's a
pain to learn the concepts and practices of C++ and I fully understand.
I don't have a specific website where all of this stuff is explained, but I can explain it some more if you want? Google and stackoverflow is always a good place to get quick answers about things.
I'll try to explain the things as best as possible.
map
The map container is one of the most common containers and is part of the C++ standard template library (STL). You can think of map as a dictionary: You can store complex "values" under specific "keys" so later on you can quickly find a value by only giving a key.
A map has two template arguments:
std::map<KEY_TYPE, VALUE_TYPE>
This means you can choose any type you want for the key, and any type you want for the value. For example, let's say you had to store the age of a bunch of people. You'd choose a string as a key, and an int for the age:
std::map<std::string, int> myMap;
Lets say Jack comes along and states he is 25 years old. You'd like to save this information in your map. You'd do it like this:
If "Jack" wasn't already in the map, a new slot is created internally. If he already existed in the map, then you'd overwrite the old value of "Jack".
Let's say your map has 1000 people in it, and you want to find Maria's age. You could write this:
int age = myMap["Maria"];
The good thing about maps: They use successive approximation for the lookup. This means they find the item you're looking for in much less than 1000 loops (O(log n)).
Iterating maps is a little more tricky, but once you understand it it makes sense. "Iterating" means to loop through every item in the map.
Maps basically store a sorted list of std::pairs. An std::pair looks like this:
template <class A, class B>
struct pair
{
A first;
B second;
};
Because myMap was declared with <std::string, int>, it will store a list of std::pairs that look like this:
struct pair
{
std::string first;
int second;
};
The "map" class contains a typedef for an iterator, and it gives you the methods begin() and end() which return the very first item and the very last item+1, respectively.
So in your for loop you have to declare an iterator of type
std::map<std::string, int>::iterator, and then assign
myMap.begin() to it. The end of the map is at
myMap.end().
for(std::map<std::string, int>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
std::cout << it->first << std::endl;
std::cout << it->second << std::endl;
}
"
it" is a pointer to the current std::pair stored in the map.
it->first will contain the current name of the person in the dictionary,
it->second will contain the age.
If you want to check if something exists in the map, you can use the find() method. find() returns an iterator.
std::map<std::string, int>::iterator it = myMap.find("Jack");
// if the iterator is pointing to myMap.end(), it means that Jack doesn't exist in the map
if(it == myMap.end())
{
// oh oh, Jack wasn't found!
}
virtual
The
virtual keyword is very easy to understand. If you are using inheritance, you absolutely need to make all methods that are overridden virtual, otherwise disaster happens.
Let's say you have these classes:
class Animal
{
public:
void makeSound() { std::cout << "I don't know what animal I am" << std::endl; }
};
class Cow : public Animal
{
public:
void makeSound() { std::cout << "Mooo" << std::endl; }
};
And here's how you instantiate them:
Animal* animal = new Animal();
Cow* cow = new Cow();
animal->makeSound();
cow->makeSound();
This would be the output:
I don't know what animal I am
Mooo
All fine and good, right?
But what if you want to store all animals in a vector (or map)? They all have to be the same type, because a vector can only store items of a single type.
Well, Cow is an Animal so we can also write this:
Animal* animal = new Animal();
Animal* cow = new Cow();
animal->makeSound();
cow->makeSound();
Great, now animal and cow are both of type Animal* and can be inserted into a vector. But wait, now the output is this:
I don't know what animal I am
I don't know what animal I am
Oh oh, why is cow->makeSound() calling the function in Animal::makeSound() and not Cow::makeSound()? Because they're not virtual! The variable
cow doesn't know it has a subclass called
Cow so it doesn't try to find any functions that belong to a subclass.
The fix:
class Animal
{
public:
virtual void makeSound() { std::cout << "I don't know what animal I am" << std::endl; }
};
class Cow : public Animal
{
public:
virtual void makeSound() { std::cout << "Mooo" << std::endl; }
};
NOTE:
It is very important to make base class destructors virtual. If you don't do this, the destructors of your derived class will not be called. This can mean disaster if your class is handling a resource.