måndag 2 juni 2014

1. C++ Basics

C++ the basics

C++ the basics

I have always looked upon people with knowledge in C++ as the true "hackers", and considered that you as a programmer really aren´t programming until you learnt to program in C/C++. I have read, as many other programmers probably have done before me, that to write an optimized program you have to write it in C/C++. And since C++ is OO and C isn´t I have also considered it to be even more profitable to learn C++ than C. This view may be true or or not, but because of it I have long had this itch for learning C++. I have mostly experience with Java from school, but I have also some experience of C and have done som basic assembly-programming in courses at my university. And besides my school programming I have mostly had experience in scripting languages such as Ruby, Python and Javascript.


Now, finally, during the spring I took a course on distance in C++ at the University of Uppsala that was very good. I feel I have learnt a lot during that time, but that may mostly be thanks to my teacher and specifically to his lecture notes. They where targeted at a experienced audience. I found that there were very little to be found similar to them online. It was my experience of lacking online-resources on C++ for experienced programmers that led me to create this blog. With experienced programmers I mean; programmers that have done some C, knows what calloc and malloc do and feel they understand the concept of pointers, they are comfortable in preferably Java or some other statically typed OO-language too.


I have created this blog mostly for the sake of documenting my own learning outcome, and also to save some other programmers time who, like me, want to learn C++ but have enough knowledge to skip the tutorials about for-loops and boolean expressions. I found my self a little bit sick of trying to open up a bunch of tabs with the different sections and articles of interest in tutorials, since a lot were too basic, and later find myself having too many tabs to feel I had gotten a thorough overview. I hope this can help them who want just the gist, without the superfluous information.

Pointers

A pointer is an adress to a memory location in the heap, or your RAM, to be more specific. All variables and objects have an adress.

int varible = 10;
int *pointer;           // not pointing at anything
pointer = &variable;    // pointing at variables adress

& operator gets the reference or the adress to a varible.

The pointer gets the reference to to the variable in this case. This means now that the pointer has the power to change the content of the memory adress. You do this by using the * operator, that says to the pointer to refer to the content and not the adress.

*pointer = 11;
std::cout << variable << endl; // outputs 11!

A pointer needs to be initialized before its used if you dont want it to use some other objects adress. In the example above we used the variable adress so we did not need to do this. When initializing a pointer you do it with the new operator. This is called dynamic allocation as it is done in runtime and not compile time.

char *c = new char;      // dynamic alloc (one char)
*c = 'a';
int size; 
cin >> size;             // get size from input
int* myArr; 
myArr = new int[size];   // dynamically alloc (array)

A common mistake is thinking that you can use uninitialized pointers.

int *ip; 
*ip = 12; // Gives error!

If we are using some other objects adress we would normally use the null-pointer until we assign 0 to the pointer to show that it is not pointing at anything:

int pointer = 0;

When you dynamically assign memory to objects you should be careful to return the memory by delete. If you don´t return memory it becomes a spaceleak in your program since no other object can now occupy the space you used.

delete c;        // deletes space pointer used.
c = 0;           // good practice
delete[] myArr;  // deletes space array used.
myArr = 0;

Pointers need to be consistent with their type. You can not use a double pointer to point at a int for example.

int i;
double *dpointer = &i // forbidden!

The * operator belongs to the variable and not to the type, this is something that could be quite confusing too.

int *a, b;  // b == int
int *c, *d; // d == int pointer

To handle this kind of thing a good practice is to use typedef.

typedef int *intPointer;  // new int pointer type
intPointer ip1, ip2;      // both are now int pointers

Arrays

You can use pointers to index an array!

int myArr[6]; 
int* myptr; 
myptr = &myArr[0]; // myptr points at myArr[0] 
myptr++;           // myptr == myArr[1]
myptr++;           // myptr == myArr[2]
*myptr = 9999;     // myArr[2] = 9999

Actually when you are passing an array to a function that expects an array the variable you pass to that function decay to a pointer of the same type as the array.

void printarray (int arg[], int length) {
    for (int n = 0; n < length; n++) {
        cout << arg[n] << " ";
        cout << "\n";
    }
}

int main ()
{
     int firstarray[] = {5, 10, 15};
     int secondarray[] = {2, 4, 6, 8, 10};
     printarray(firstarray, 3); 
     printarray(secondarray, 5);
     return 0;
}

Here firstarray and secondarray in the function call decays to int pointers to the first element in the array. The expression int sum( int a[], int size); == int sum( int* a, int size);. You could also therefore do the following kind of thing as well.

int myArr[6]; 
int* myptr; 
myptr = myArr; // myptr and myArr points to same thing.
myptr[4]=314   // myptr can now be used the same way.
*(myArr+5)=315 // myArr can also be used as an pointer.

The two ways seem almost identical but with a small difference. The sizeof cannot be used on pointers, and you can not use the myArr variable like a pointer in increasing and decreasing the index.

sizof(myArr);   // the size of an array in bytes.
sizeof(myptr);  // space for the pointer in bytes.
myArr++;        // not allowed!

Matrixes

dynamic allocation

In C++ you cannot initialize an matrix dynamically in one statement like in other statically typed languages. Initialization of a matrix can only be done statically.

int *a = new int[3][3];   // forbidden!
int **b = new int[3][3];  // also forbidden!
int **c = new int*[3];    // OK!

What you need to do is to allocate an outer array of inner arrays first, and then in a loop allocate the arrays to the outer array. Tedious, I know, but this is one of those "facts of life"-moments.

int **mtrx = new int*[3]; 
for ( int i = 0; i < 3; i++ )
{
    mtrx[i] = new int[3];
}

And to delete the matrix you need to do it in the reverse order.

for ( int i = 0; i < 3; i++ )
  delete[] mtrx[i];
delete[] mtrx;

By using typedef in this context the benefits of typedef become much clearer.

typedef int intArray[3];  // defined type int[3]
intArray *mtrx = new intArray[3];  // matrix pointer 3*3
delete[] mtrx; 
// delete whole matrix in one statement because we used only one new.

Of course, you could still declare a matrix statically. Lets look at how its done.

static allocation
int[3][3] mtrx = {{1,2,3}, {4,5,6}, {7,8,9}}; //Ok

And the single statement int[3][3] mtrx1; is equivalent to int[9] array;. The reason for this equivalence is that in both cases the memory is allocated consecutively. When you dynamically allocate memory you do it where ever the memory manager finds memory available, meaning not necessarily consequetly. If you declare an matrix:

double a[40][30];

Now 1200 ints have been consecutively placed in memory. Then to find element a[i][j] it uses the formula a+j+i*30 to find the element. This means that the compiler does not need to know the size of the outer dimension, just the inner (30). Therefore you often see declarations like this:

int func( int rader, double a[][30]);

If you don´t statically initialize the array like above the content is garbage but if you would just initialize some part of it the rest would be initialized to 0:

int[3][3] mtrx = {1}; // mtrx[0][0] == 1, rest 0
int[3][3] mtrx2 = {}; // all 0

References

References are some kind of pointers too, but they must always refer to something, and cannot like pointers point to nothing. A common mistake with references is confusing the adress operaton & and the reference operator & that look the same.

int a = 10;
int *pointer = &a; // adress operator

Here the & refers to an adress. The reference operator & is mostly used as a parameter in functions to have an alias for the same varible.

void swap( int& a, int& b) // reference operators
{
  int c = a;
  a = b;
  b = c;
}

int i = 7;
int j = 3; 
swap(i, j); // i = 3, j = 7

Const and pointers

Some explanations regarding const and pointers.

const int a = 5; 
int b = 5;        

const int *p = &a; // a pointer to a const, 
                   // does not allow modification of the data through the pointer
*p = 4;     // Not allowed!

int* const pp = &b; // a const pointer, this pointer need to be assigned a value immediately.
                    // does not allow modification of the adress of the pointer
*pp = 5;    // Ok! 

const int *const ppp = &a;  // neither ppp or *ppp can change

Namespaces and the structure of C++ programs

Namespaces can be used to structure a program into "logical units". For instance, if you had a program that connected to the Internet, you might have a namespace to handle all connection functions.

the headerfile netconnect.h

namespace net_connect
{
  int make_connection();
  int test_connection();
  //so forth...
}

If you now include the header file that contains the namespace in your program file with #inlude "netconnect.h" you can call a function from the namespace with the scope operator ::.

net_connect::make_connection();

You can also introduce an entire namespace into a section of code by using a using-directive with the syntax:

using namespace net_connect;

Doing so will allow the programmer to call functions without having to specify the namespace of the function while in the current scope. (Generally, until the next closing bracket, or the entire file, if you put this in the global scope). This convenience can be abused by using a namespace globally, which defeats some of the purposes of using a namespace. You can typically see std being used this way to be able to handle IO without ever having to specify std::

using namespace std; 

Finally, you can introduce only specific members of a namespace using a using-declaration with the syntax

using namespace_name::thing;

Another trick with namespaces is to use an unnamed namespace to avoid naming conflicts. To do so, simply declare a namespace with the normal syntax, but leave off the identifier; when this is done, you will have

namespace
{
  //functions
}

and within the namespace you are assured that no global names will conflict because each namespace's function names take precedence over outside function names. Great!

Here follows an example of the usage of namespaces and also of how to structure a small C++ project.

The file msg.h (definitions)

#ifndef MSG_H
#define MSH_H

#include <iostream>
#include <string>

namespace msg
{
  void write( string msg );  // definition
}

#endif

The file msg.cpp (implementation)

#include "msg.h"

namespace 
{ 
  string header()
  { 
    return string("Info: "); 
  }
}

void msg::write( string msg )    // implementation
{ 
    cout << header() << msg << endl; 
}

The file error.h

#ifndef ERROR_H
#define ERROR_H

#include <iostream>
#include <string>

namespace error
{
  void write( string msg );
}

#endif

The file error.cpp

#include "error.h"

namespace 
{
  string header()
  { 
    return string("Error: "); 
  }
}

void error::write( string msg )
{ 
    cout << header() <<  msg << endl; 
}

And our example program:

#include "msg.h"
#include "error.h"

int main()
{
  msg::write("this is a message");
  error::write("this is an error");

  int smallerthan10(int i)
  {
    using namespace error;
    if(i > 10) {
        write("it was bigger!"); //will use error write.
        return 0;
    } else {
        msg::write("it was smaller!"); //will use msg write.
        return 1;
    }
  }
}

Intro to Classes, inline and the static keyword

We will not go into the details of how to work with classes but assume that you know what a class is. We will only show you the structure of a class. In the next chapter we will dwelve into how to work with classes in more detail.

If a function is inline, the compiler places a copy of the code of that function at each point where the function is called at compile time. The function should be compiled inline into where it is used (avoiding a function call overhead).

inline int Max(int x, int y)
{
   return (x > y)? x : y;
}

int main( )
{
   cout << "Max (20,10): " << Max(20,10) << endl;  //is now replaced at compile-time
   return 0;
}

If the definition of a method comes directly in the .h file the method is treated like an inline method, therefore when we define the function in the .h file and not only declare it we say we write the definition inline. In the following class declaration, the Account constructor is an inline function. The member functions GetBalance, Deposit, and Withdraw are not specified as inline but can be implemented as inline functions.

class Account
{
public:
    Account(double initial_balance) { balance = initial_balance; }
    double GetBalance();
    double Deposit( double Amount );
    double Withdraw( double Amount );
private:
    double balance;
};

inline double Account::GetBalance()
{
    return balance;
}

inline double Account::Deposit( double Amount )
{
    return ( balance += Amount );
}

inline double Account::Withdraw( double Amount )
{
    return ( balance -= Amount );
}

The staic keyword is a little bit trickier. It can be used in many contexts. static can be used in three major contexts: inside a function, inside a class definition, and in front of a global variable inside a file making up a multifile program.

If used inside a function it simply means that once the variable has been initialized, it remains in memory until the end of the program. You can think of it as saying that the variable sticks around, maintaining its value, until the program completely ends.

for(int x=0; x<10; x++)
{
  for(int y=0; y<10; y++)
  {
    static int number_of_times = 0;
    number_of_times++;
  }
}

In classes it can have different uses. It could be a variable that for example counts the number of the instances created of the class. If a method have the static keyword it could be a method that simply isn´t specific to a instance but to the class in general.

class User
{
  private:
    int id;
    static int next_id;

  public:
    static int next_user_id()  
    // declared static to be able to use next_id
    {
        next_id++;
        return next_id;
    }
    user()
    {
        id = User::next_id++;    // or, id = User.next_user_id();
    }
};
int User::next_id = 0; 

Notice that the next_id variable is initialized outside of the definition of the class. In fact, this must even be outside of the .h file or else you will get errors.

Exceptional programming

An exception is a situation in which a program has an unexpected circumstance that the section of code containing the problem is not explicitly designed to handle.

Exception handling in C++ propagates the exceptions up the calling-stack; therefore, if there are several functions called, but only one function that needs to deal with the errors, the intermidiate functions don't need to return error codes and can be totally oblivious of the errors that bubble up the stack.

When an errors occur, the function generating the error is said to throw an exception.

#include <stdexcept>

double& Vector::operator[](int index)
{
  if(index < 0 || index >= size) 
     throw out_of_range("Vector::operator[]"); // exception thrown
  return arr[index];
}

You can actually throw any object, even int, char and char *. If you include stdexcept we have access to many predefined exceptions like out_of_range. The exceptions you use inherit from either logic_error or runtime_error. You either use these or extend these classes to construct your own.

When you throw exceptions you usually use the try-catch structure. With one try block you can have several catch blocks.

try {
   // protected code
} catch( ExceptionName e1 ) {
   // catch block
} catch( ExceptionName e2 ) {
   // catch block
}catch( ExceptionName eN ) {
   // catch block
}

When you use a catch block a good practice is to catch a general exception so that you cover other cases aswell, maybe an exception thrown from a library function.

#include <iostream>
using namespace std;

int main( )
{
   try 
   {
      // do something
      // if that something was not logic =>
      throw logic_error( "logic error" );
   }
   catch ( exception &e ) // general exception
   {
      cerr << "Caught: " << e.what( ) << endl;
      cerr << "Type: " << typeid( e ).name( ) << endl;
   };
}

Output:

Caught: logic error
Type: class std::logic_error

Where to catch an exception should be given some consideration. Sometimes you just might want to throw from the function and catch at the top of the callstack.

class Student {
public:
  Student( char *namein, char *coursein) : name(0), course(0) 
  {
    try {
        name = new char[strlen(namein)+1]; strcpy(namn, namein);
        course = new char[strlen(coursein)+1]; strcpy(course, coursein);
    } catch (bad_alloc &ba) {
        delete[] name; // if not successful
        throw;         // and throw up the callstack...
    }
  }
private:
  char *name;
  char *course;
};

int main()
{
  try {
      Student student("Patrik", "OOP with C++");
  } catch (bad_alloc &ba) { // final destination of exception
      cout << "ERROR " << endl;
  }
}

Here is an example of how to make usage of a homegrown exception.

#include <iostream>
#include <exception>
using namespace std;

class myexception: public exception
{
  virtual const char* what() const throw()
  {
    return "My exception happened";
  }
} myex;

int main () {
  try {
    throw myex;
  } catch (exception& e) { // we catch anything that passes
    cout << e.what() << '\n';
  }
  return 0;
}

Inga kommentarer:

Skicka en kommentar