C++ Learning Note

List

#include <list>

In C++ lists are ordered sequences of variables of the same type. To initialize them we have to specify which type are the variables inside it. For example, for a list of integer values:

list<int> numbers_list({1,10,100,1000});

And a list of string values:

list<string> vocals_list( {"a","e","i","o","u"} );

The inconvenience with lists in C++ is that they are not as easy to print as in other languages. In this case we need to loop over all the items in a list to print them one by one. For now, here’s an example of printing a list:

for (int val : numbers_list)             // Loop
    cout << val << "  ";                 // Print function

for (string val : vocals_list)           // Loop
    cout << val << "  ";                 // Print function

Lists are very useful, as they occupy a memory space that can be modified. They have builtin functions to, for example, add a new item in the beginning of the list, or at the end of it:

numbers_list.push_front(0);             //insert in the beginning
numbers_list.push_back(3000);           //insert in the end

The resulting list would be 0,1,10,100,1000,3000

Finally, we can also concatenate a list at the end of another, enlarging the first one and not deleting the second one, with the builtin function insert():

list<int> new_list({5,50,500});

numbers_list.insert(numbers_list.end(),new_list.begin(),new_list.end());

Then the numbers_list will be modified as 0,1,10,100,1000,3000,5,50,500

Dictionaries

#include <map>

A dictionary in C++ is called a map, and it is a container of values that are indexed by a key. This means that it stores two kinds of information: keys and values.

For example, if we want to store the names of a TV series characters and also in how many episodes they appear, we don’t need a list with the names and a list with the number of episodes, we just need a dictionary where the keys are the names and the values are the number of episodes: { "Dolores": 30, "Maeve": 27, "Theresa":6, "Clementine":11 }  { "Dolores": 30, "Maeve": 27, "Theresa":6, "Clementine":11 } 

To initialize it we need to call map, and specify the data types of the keys and values:

map<string,int> girls_dictionary;

Here we are creating a dictionary called girls_dictionary, where the keys are strings and the values are integers.

To insert data into this dictionary we can call each key and assign it a value, one by one:

girls_dictionary["Dolores"] = 30;
girls_dictionary["Maeve"] = 27;
girls_dictionary["Theresa"] = 6;
girls_dictionary["Clementine"] = 11;

for (auto item : girls_dictionary)
    cout << item.first << " appears in " << item.second << " episodes\n";

Finally, we can print the items in the dictionary with a loop. When we print it this is not a list, the dictionary itself organizes the keys alphabetically.

map<double, float> x_t_dictionary;
x_t_dictionary[t_0] = x_0;
x_t_dictionary[t_1] = x_1;

// The functions ROS_INFO() and ROS_INFO_STREAM() essentially have the same purpose as 'cout'
for (auto item : x_t_dictionary) {
  ROS_INFO_STREAM("Time " << item.first << ", position " << item.second
                          << " \n");
  }

I/O functions

Print

  • printf
  • cout

Input

  • cin
  • getline(cin, name)

Arrays

int myarray[6];
int myarray[6] = {4,8,15,16,23,42};
int myarray[] = {4,8,15,16,23,42};

We can also print all the elements of an array with a for loop:

for (auto items : myarray) {
    cout << items << endl;
}

Pointers

int myvariable = 42;

We have already seen how to obtain the address of a variable using &. The type of variable that can store this address of another variable is called a pointer. Pointers can be declared with the operator * , specifying the type of variable they will be pointing to:

int * mypointer;

If we want this pointer to store the address of our previous variable myvariable, we just need to give it the value of its address:

mypointer = &myvariable;
cout << mypointer << endl;

This expression will print the previous address of myvariable we’ve seen in the first section:

> 177687

Now that the relationship between myvariable and mypointer is established, we can access the original value of myvariable just using its pointer:

cout << *mypointer << endl;

This will print the value stored in the memory with the address 177687:

> 42

We call this operation dereferencing, using the symbol *, and it is always followed by a name of a variable. Do not confuse the asterisk of declaring a pointer, which follows a data type, with this dereferencing operator!

  • Why this is useful?
    • We can first declare a variable, then assign a pointer to its address, and just manipulate this pointer to make changes.
    • Also, we cannot change the type of myvariable during a program, but a pointer can be assigned to another variable later in the same program.

Constants

Pointers may also be used only to access values to read them, not to modify them. These will be pointers pointed to constant variables, which need to be specified when declaring the pointer.

For example, for an integer variable:

int variable = 10;

when declaring the pointer we need to specify that it points to a constant integer:

const int* pointer = &variable;

We can read the value of the variable it is pointing to by dereferencing:

cout << "Value of variable: " << *pointer << endl;

But we cannot change the value of the variable:

*pointer = 20; // INCORRECT!!!!

We can also create constant pointers, independently of them pointing to a constant variable or not. This kind of pointers need to specify the constant character right before the pointer’s name:

int* const pointer;

To make this even more trickier, here are all the possibilities we can have using constants:

int* pointer;                   // non-constant pointer to non-constant integer
const int* pointer;             // non-constant pointer to constant integer
int* const pointer;             // constant pointer to non-constant integer
const int* const pointer;       // constant pointer to constant integer

The final thing we need to learn is that in C++ functions CANNOT return arrays. It is just not possible. What they CAN do is return a pointer to an array. See how useful they are? Awesome.

In this exercise first we are going to show you a small piece of code of the RosbotClass we’ve been using all along:

float *RosbotClass::get_laser_full() {
  float *laser_range_pointer = laser_range.data();
  return laser_range_pointer;
}

This function called get_laser_full() is a function that returns a pointer (see the operator asterisk before the name of the class **RosbotClass**). What it does is take the values of a ROS vector called laser_range and assign them a pointer, then return this pointer.