You are on page 1of 7

A:hover { color: orange }

Ten Things on c++


1. basic compilation
Given the basic program:
// prog1
#include <stdio.h>
int main(int argc,char** argv)
{
printf("hello world\n");
return 0;
}

We compile and run it:


% g++ -c prog1.cpp -o prog1.o
% g++ -o prog1 prog1.o
% ./prog1
hello world

A better way to do this is using a "makefile" to link together all the dependancies:
EXES = prog1
all

: $(EXES)

full : clean all


clean :
find -name "*.o" -exec rm {} \;
rm -f ${EXES} -R
prog1 : prog1.o
g++ -o prog1 prog1.o
prog1.o : prog1.cpp
g++ -c prog1.cpp -o prog1.o

we then compile the program by just typing "make".

2. modularisation
A cpp program consists of "header" files (.h) and "cplusplus" files (.cpp). Header files contain function
specifications, e.g.
void print_hello(int nTimes);

cplusplus files contain the implementations, e.g.


void print_hello(int nTimes)
{
for (int i=0; i<nTimes; i++)
printf("hello world\n");
}

The idea of using headers is that other programs can include them, know about the function specifications,
but not the implementation. The implementation is added during the linking; i.e. we link to the object file
containing the implementation.

3. procedural programming
Like most modern languages, c++ permits structured procedural programming. Typically, operations that
are replicated a lot in the code should be abstracted out to their own function. A function can return
nothing (void in c++), return an ordinal type (e.g. int), a pointer (e.g. int*), or an object. A function
without a return type can be called a procedure. Example function specifications:
void print_hello(int nTimes);
int random_integer(int low,int high);
float compute_fraction(int num,int total);

As a rule-of-thumb, a function should be about a page-of-text long.

4. references and variables on the stack


c++ comes with a number of "ordinal" (or standard) variable types: int float char double. These can be
formed into arrays. When "instantiating" (or creating) a variable in the normal fashion, the variable is
placed in the local function stack. When that stack is exited then the variable goes out of scope and is no
longer available. Note that a new stack starts for every { and ends at the corresponding }.
int i = 10;
{
int j = 20;
}
printf("%d and %d\n",i,j);

// will not work because j has been destroyed

This is also the case when passing variables to functions. When a function is called, the current function
stack is pushed onto the program stack and a new function stack created. When the function exits, that
new function stack is destroyed and the function stack on the program stack is retrieved (by popping it). If

you make any changes to the variable value in the called function then that change is lost (passing by
value):
void increment(int num)
{
num++;
}
int main()
{
int num = 10;
increment(num);
printf("%d\n",num);
}

// still outputs 10

In this case, when increment was called a *new* value of num was created locally in the increment
function. num was passed by value and the change was forgotten in the main program. To get over this,
we pass by reference. We make one and only one change to the code:
void increment(int& num)

and now the output will be 11. The increment function did not create a new value of num, rather it created
a "reference" to the existing variable.

5. pointers and variables on the heap


Another way to solve the previous problem would be to send the "address of" the variable to the function,
and the function operate on a "pointer". The code would be:
void increment(int* pnum)
{
*pnum++;
}
int main()
{
int num = 10;
increment(&num);
printf("%d\n",num);
}

// dereference the pointer

// & is address-of
// output is 11

In this example, in main we use &num to send the address-of the variable to the function. This address is a
pointer in the increment function. To access the data that a pointer is pointing to, you have to dereference
the pointer using *.
You can do pointer arithmetic, i.e. you can go pnum++ and you wont increment the value. Rather, you
will increment the pointer which will now point to the next address in memory. *pnum++ is needed
because you must dereference the pointer to change the value it is pointing at.

void increment(int* pnum)


{
*pnum = 10;
pnum++;
*pnum = 20;
}

// pointer is incremented (to next memory address)

int main()
{
int num[2];
increment(num);
printf("%d %d\n",num[0],num[1]);
}

// dont need & because an array is a pointer


// output is 10 20

We can instantiate variables outside of this stack framework by dynamically creating them in the heap.
They exist on the heap until they are destroyed:
int* pnum = new int;
*pnum = 10;
delete pnum;

A new command allocates memory in the heap. A delete command destroys that memory. When using
new/delete you have to be careful to make sure you have the same number of each, otherwise your
program will acquire memory leaks.
You might want to allocate large data on the heap because your program will operate quicker, e.g. if you
have float[10000] then it is best to leave it on the heap rather than pushing/popping it on the stack
everytime you call a new function!

6. input and output


Basic program io is achieved by (a) printf outline to stdout, (b) command line parameters, argc and arv, (c)
by reading/writing files.
(a and b) argc gives the number of parameters contained in argv. The first argv always contains the name
of the program.
int main(int argc, char** argv)
{
int num = atoi(argv[1]);
printf("%d parameters, first is %d\n",argc-1,num);
}

Calling this program with "prog 10 20" produces the output "2 parameters, first is 10". Note that arrays in
c++ start at zero, so we are ignoring the first value argv[0] because it is the program name.
(c) we open a file using fopen, read the contents into a buffer using fgets, and close the file using fclose:

FILE* pfile = fopen("prog1.cpp","r");


char buffer[100];
int line(0);
while (fgets(buffer,100,pfile)!=NULL)
printf("%d: \t %s",line++,buffer);
fclose(pfile);

To write to a file we using the w flag to fopen, and use


fprintf(pfile,"hello\n");

7. objects and classes


A class is a blueprint for an object. A class is a complex abstract data type that we can design and use in
our programs. They "encapsulate" simple data (ordinal types) along with member functions to operate on
it. External functions and objects then do not have access to the data: they have to go through the member
functions. This implements a very important concept: data hiding, which reduces the risk of errors later on.
class CStudent
{
public:
CStudent();
~CStudent();

// called when instantiated


// called when destroyed

void DoLogicTest(int answera,int answerb);


void PrintLogicTestScore();
private:
int nLogicScore;

// member variable

};
int main()
{
CStudent andrew;
andrew.DoLogicTest(10,20);
andrew.PrintLogicTestScore();
}

In this example, the "CStudent" is a class and "andrew" is an object of that class. In the main() function,
the program cannot access nLogicScore: it has to go through the public functions. You could make
nLogicScore "public", but you wouldnt want to because it would compromise the design of the class (and
external elements could alter the scores!). This example demonstrates data hiding and encapsulation.

Constructors and Destructors are crucial. If we have elements on the heap for an object, then (a) we need
to allocate the memory for them in the constructor (or somewhere in one of the members if needbe), (b)
we absolutely need to deallocate the memory in the destructor (because this is your last chance, after this
the pointers will get thrown away).

8. advanced oop
A very attractive property of object-oriented programming is that you can create classes that inherit
functionality from other (base) classes. This is called inheritance. For classes/objects we can also
implement basic operators, like ==, to make them easier to use. This is called operator overloading.
class CPerson
{
public:
CPerson();
~CPerson();
void ComputeAge(char* szDateOfBirth);
int GetAge();
private:
int age;
};
class CStudent : public CPerson
{
public:
CStudent();
~CStudent();
void DoLogicTest(int answera,int answerb);
void PrintLogicTestScore();
private:
int nLogicScore;

// member variable

};
int main()
{
CStudent andrew;
andrew.DoLogicTest(10 20);
andrew.PrintLogicTestScore();
andrew.ComputeAge("01 April 1985");
printf("andrew is %d years old\n",andrew.GetAge());
}

In this example, the "CStudent" class inherits from "CPerson". This means, that objects of class CStudent
inherit all the functionality from the CPerson class (without replicating the code!). This is very useful if
you can design abstract classes which are reused a lot: it can save you a lot of time.
We perform operator overloading by adding:

CPerson& operator = (const CPerson& rhs);


bool operator == (const CPerson& rhs) const;

9. error handing
c++ comes with a number of techniques for error handling and preventing bugs:
(a) a c++ program returns 0 if it was successful, !0 otherwise,
(b) you can use assertions to check that incoming parameters to your functions are within allowed ranges.
It is estimated that 10% of all lines of code in MS Windows are assertions! (Source: Professor Sir Tony
Hoare, Microsoft Research).
(c) you get try { function } catch(...) { error procedure } error handing to catch errors as they occur.

10. the standard template library


c++ doesnt come with a nice string class (we have to use char*) or a nice array class (we have to use int
num[10]). However, these are implemented in the standard template library (STL).
#include <vector>
#include <string>
using namespace std;
int main();
{
string szName = "hello";
szName += " world";
vector<int> nums;
nums.push_back(10);
nums.push_back(20);
for (int i=0; i<nums.size(); i++)
printf("%d: \t %d\n",i,nums[i]);
}

Andrew Graves
November 2004.

You might also like