Search

Sponsored Links

Meta

Categories

Archives

Recent Posts

RSS Feeds

06
Dec

C++ Basics Tutorial - Lesson 1

Related Blog Items

C++ Basics - Lesson 1
1. C++ Basics
1.1 Advantage of Structured Programming
1.2 Interpreter and Compiler Program
1.3 Escape Sequence
1.4 Namespace
1.5 Advantages of OO Programming
1.6 Library math.h
1.7 Function Prototype
1.8 Block
1.9 time(0)
1.10 Random Number Generation
1.11 Enumeration
1.12 Global Variables
1.13 Constant Global Variables
1.14 Creation and Deletion of Global Variables
1.15 Name Conflict Between Local and Global Variable
1.16 Storage Class
1.17 Scope
1.18 Recursion
1.19 Recursion Exercise 1 - Binary Search of an Array
1.20 Recursion Exercise 2 - Towers of Hanoi
1.21 Order of Evaluation on Operands
1.22 Constant Variable
1.23 Principle of Least Privilege (PLP)
1.24 Inline Functions
1.25 Reference
1.26 Default Arguments
1.27 Overloading Functions
1.28 EOF
1.29 New Line

1. C++ Basics

1.1 Advantage of Structured Programming

Although it is a trend to use object-oriented programming, structured programming also has its advantage. Internal structure of objects are often best built with structured programming techniques. Also, the logic of manipulating objects is best expressed with structured programming..

1.2 Interpreter and Compiler Program

Interpreter programs were developed that can directly execute high-level language programs without the need of compiling these programs into machine language. It is used on cases that programs are frequently updated. Once the program is developed, a compiled version will be produced to run quickly..

1.3 Escape Sequence

“\” is escape character. It together with the following character forms an escape sequence used in output statement. They are used to control the printer and are not printed out.

“\n” change to a new line. It is a good common practice to add it in the end of all printing statements.

“\t” horizontal tab.

“\r” carriage return - return the cursor to the beginning of current line.

“\a” alert. Sound the system bell.

“\\” used to print out a “\”.

“\”" used to print out a double quote.

1.4 Namespace

#include <iostream>;
 
using namespace std;
int main( )
{
  cout << "Welcome! \n";
  std::cout << "Welcome! \n";
  return 0;
}

A namespace is a group of software components with the same name prefix - e.g., std. Each header file in the C++ draft standard uses a namespace called “std” to guarantee that every feature provided by the C++ standard library is unique from software components developed by other programmers. The statement above simply says that we are using software components from the C++ standard library, such as cout. If we use the namespace in front of each software component, then we needn’t the statement at the top. If we want to define our own class library, we should use our own namespace.

1.5 Advantages of OO Programming

OO programming has a lot of advantages over procedure programming, many of them are achieved by data encapsulation.

Procedure programming can also call existing functions to achieve code reusing, but functions and data are separate - in other words, functions are stateless - they don’t remember their own state across different calls. Whenever a function is called, all data to be manipulated have to be passed to and returned by the function. Data are therefore exposed to programmers, who may manipulate the data wierdly or carelessly. Besides, a single piece of data may be passed around and handled by a lot of functions within a large project. When an error finally emerges, it may be very difficult to find out where the error happens.

In comparison, OO programming with data encapsulation can avoid these problems. Because data are encapsulated with functions to form an object, functions can remember their own state and their results. There is no longer need to pass this part of data to these functions every time they are called. Sensitive or private data can be therefore isolated from outside world. If anyone wants to do anything on the data, they have to do it through the object’s functions, which can be well defined and error-proof.

This simulates real world objects better. Take a man as an object. This object can have a data members such as body temprature. Other people can not directly change his body temprature without going through his own cooperation and physical reaction. So body temprature is a private data members isolated from the public. This isolation eliminates the possibility that other programs may accidentally change an object’s own sensitive data inproperly.

A well-written class is cohesive and self-contained. It contains all necessary data to similate the real-world problem, all necessary functions to manipulate these data and provide services to outside world. It exists and functions quite independently, reducing the coupling with outside world to the lowest extent, and therefore may be reused in different occasions.

An object “knows” how to behave itself properly - how to construct and initialize itself, how to response to different kinds of external messages properly, how to destroy itself, etc.. Once a class is properly written and fully tested, other programs can simply make use of it to perform a certain task, and will have little chance to encounter any error. This greatly reduces the complexity of programming and possibility of errors.

So generally speaking, OO programming and data encapsulation has the following advantages:

1. Reduces data exposure: because private data are encapsulated within objects, when their functions are called, less data need to be tranmitted. These reduces the exposure of data and the chance of errors.

2. Clearer responsibility for data: because each object is responsible for his own private data and others can not wrongly change it, when a piece of data is wrong, we easily know who is doing the wrong thing.

3. Less error-prone: if a project uses only those well-written and fully tested classes, it is much easier to organize, modify, debug and maintain than those written in procedural languages. The bigger the size, the more obvious the advantage.

4. More stable for clients: when a class’s implementation is changed, as long as its function signatures are not changed, a program which uses this class will not be affected.

5. Enhances software reuse: by composition (including other object as data member) and inheritance (build a new class based on a base class), software reuse is greatly strengthened.

1.6 Library math.h

Consists of all mathematical functions:

ceil (x) ceil (9.2) = 10

cos (x)

exp (x) ex

fabs(x) absolute value of x

floor (x) floor (9.9) = 9

fmod (x, y) reminder of x/y as a float. fmod (9.85, 3.2) = 0.25

log (x) log ex

log10 (x) log 10x

pow (x, y) x y

sin (x)

sqrt (x) sqrt (9) = 3

tan (x)

1.7 Function Prototype

The function prototype is placed outside any function including “main”, right after the “#include” preprocessor directive. That’s why function prototype has file scope.

Function Prototype is something FORTRAN and earlier versions of C do not have. It is designed to enable the compiler to know what kind of a function it is before a function is called. That’s why when the function body is before the calling statement the function prototype is not necessary. It also enables the compiler to find out whether a function is properly called i. g. with wrong numbers of arguments, and convert the parameter type if the arguments supplied is not correct.

You can include the names of the parameters into the function prototype to make it look more clear, but the compiler will ignore the parameters.

1.8 Block

The declarations and statements enclosed in braces ” { }” is called a block. A block is just a compound statement which includes declarations. A block instead of a function is the smallest combination of statements in C++. You can create a block anywhere you like - just enclose at least one declaration and one statement with braces.

The variables declared in a block has a “block scope” - they are hidden outside this block. Variables declared outside this block in the same function will still be “visible” inside this block (so are global variables, of course). So the character of a block is different from a function: a function is of two-way information hiding, while a block is of one-way only.

1.9 time(0)

This function returns the current calendar time in seconds. Its header file is “time.h“.

1.10 Random Number Generation

unsigned seed;
 
cin << seed;
srand(seed); // or srand (time (0) )
y = a + <strong>rand</strong>( ) % b;

=>Pseudo-random sequence

Function “rand” returns a pseudo-random sequence of integer between 0 and RAND_MAX (a symbolic constant defined in the “stdlib.h“). The function prototype of “rand” is in this header file “stdlib.h“. The numbers generated by “rand” is actually a fixed sequence. Each time you run this program, you get the same sequence. To change this sequence, you have to use another function “srand” to “seed” the “rand” function with different unsigned integer. Its header file is also “stdlib.h“. For each integer a unique and fixed sequence is generated by “rand”.

=>Scaling

The function to generate a random sequence of number within a certain range is called “scaling”:

n = a + rand () % b

the random numbers are from a to a + b.

1.11 Enumeration

An enum is an alias of an integer. It setups up a corresponding relationship between a specific integer and a user-chosen identifier. Instead of having to remember what an integer represents, the programmer can easily remember the self-explained alias. For example, a set of integers from 0 to 7 represents color black, blue, green, cyan, red, magenta, brown and white. After we define

enum Color {Black, Blue, Green, Cyan, Red, Magenta, Brown, White};

we can always use “Black” instead of 0, but the compiler will just treat “Black” as 0.

Now “Color” is a user-defined type. You can use this type just like int, float to declare variables, but these variables can only have the values enclosed in { }:

Color c1, c2
c1 = Green;
c2 = Brown;
if(c1 == Red)...;

You can also join the definition of the enum type and declaration of its variables together:

enum {Black, Blue, Green, Cyan, Red, Magenta, Brown, White} c1, c2;

You can assign enums to both variables of their own types, or simply integer variables:

enum {Black, Blue, Green, Cyan, Red, Magenta, Brown, White};
int c1, c2
c1 = Green;
c2 = Brown;
if(c1 == Red)...;

By default, the first enum enclosed in { } has the value of 0, the next 1,… , unless specifically defined:

enum {Black, Blue, Green, Cyan = 23, Red, Magenta, Brown, White};

Then Black = 0, Blue = 1, Green = 2, Cyan = 23, Red = 24, Magenta = 25,…

1.12 Global Variables

Global variables are variables declared outside any block including main function. They are visible in all blocks in all files in the same process. In files other than the one where the global variable is defined, you have to use keyword extern to tell the compiler: “The following global variable whose name is xxx and whose type is xxx is defined elsewhere, and you have to find out where yourself.”

Global variables can be defined in either a header file or a source file. But if you define a global variable in a header file, then when more than one files include that header file, multiple copies of the same global variable will be instantiated, and you will have link errors. So you should normally put the definition of global variables in a source file.

f3fb284d1b46439f0fa46350c48836d7007

Output will be:
1 2 3
1 2 3

But the above approach is not what people normally do. Because this approach requires each file which uses those global variables to declare all of them which keyword extern one by one, causing redundant code and is error-prone. The normal approach is to create a separate header file and put all extern declarations of the global variables in it. Logically this header file should have the same name as the source file, but it can be different (as shown in the following example). Then all files which accesses the global variables can simply include this header file:

//globals.cpp
#include "stdafx.h"
 
int array[3] = {1, 2, 3}; // array is the global object
int ANY = 4;
 
//Any.h
 
#ifndef _ANY_H
#define _ANY_H
 
extern int array[];
extern int ANY;
#endif
 
//A.cpp
#include "stdafx.h"
#include "A.h"
#include <iostream.h>;
 
#include "Any.h"
 
A::A()
{
  cout << array[0] << " " << array[1] << " " << array[2] << " " << ANY << endl;
}
 
//B.cpp
#include "stdafx.h"
#include "B.h"
#include <iostream.h>;
 
#include "Any.h"
 
B::B()
{
  cout << array[0] << " " << array[1] << " " << array[2] << " " << ANY << endl;
}
 
//main
#include "stdafx.h"
#include "a.h"
#include "b.h"
 
int main(int argc, char* argv[])
{
  A a;
  B b;
  return 0;
}

Output will be:
1 2 3 4
1 2 3 4

1.13 Constant Global Variables

In a sense, the reason you want to use global variable is to pass data between several objects which do not have much coupling between them. Therefore global variables should normally not be constant. When you use a constant global variable, you actually want a symbol or an alias for a constant. Therefore it is clearer to use a #define to define this constant. For this reason, C++ compiler would not support syntax “extern const …“. If you want to have a constant global variable, you should put it in the header file (Any.h). Compiler will treat it in the same way as a #define.

1.14 Creation and Deletion of Global Variables

The global object’s constructors are called before any program is execuated, and their destructors are called after all program ends.

//A.h
class A
{
  public:
  void Say();
  A(char * name);
  virtual ~A();
private:
  char * m_name;
};
 
//A.cpp
#include "stdafx.h"
#include "A.h"
 
A::A(char * name) : m_name(name)
{
  char buf[80];
  sprintf(buf, "Constructor of %s\n", m_name);
  printf(buf);
}
 
A::~A()
{
  char buf[80];
  sprintf(buf, "Destructor of %s\n", m_name);
  printf(buf);
}
 
void A::Say()
{
  char buf[80];
  sprintf(buf, "My name is %s\n", m_name);
  printf(buf);
}
 
//B.h
class B
{
  public:
  B();
  virtual ~B();
};
 
//B.cpp
 
#include "stdafx.h"
#include "B.h"
#include "A.h"
 
A g_b("GLOBAL_IN_B");
 
B::B()
{
  char buf[80];
 
  sprintf(buf, "Constructor of class B\n");
  printf(buf);
}
 
B::~B()
{
  char buf[80];
 
  sprintf(buf, "Constructor of class B\n");
  printf(buf);
}
 
//Test.cpp
#include "stdafx.h"
#include "a.h"
 
A g_main("GLOBAL_IN_MAIN");
 
extern A g_b;
 
int main(int argc, char* argv[])
{
  printf("\nBeginning of main!\n");
  g_b.Say();
  printf("End of main!\n\n");
  return 0;
}

The output will be:

Constructor of GLOBAL_IN_MAIN
Constructor of GLOBAL_IN_B
Beginning of main!
My name is GLOBAL_IN_B
End of main!
Destructor of GLOBAL_IN_B
Destructor of GLOBAL_IN_MAIN

Because global objects are created before any code is executed, they must not be any resource that can not be initialized “on the spot” and has to be initialized by the program. If you have to have a global resource like that, create a global reference (pointer) to the resource, which can be initialized to NULL on the spot, then create and initialize the resource in the program.

1.15 Name Conflict Between Local and Global Variable

If in a block a local variable of the same name as the global variable is declared, the local variable will suppress the global one from the declaration line onwards. To access the global variable from this block, use unary scope resolution operator “::” in front of the identifier.

int x = 10;         // Global variable
 
int main ( )
{
  int x = 4, y;
  y = x/ ::x;       // Value should be 0.4
}

1.16 Storage Class

Each variable or object has its attributes including storage class, scope and linkage.

=>auto

Variables of automatic/local storage class are created when the block in which they are defined is entered, and destroyed when the block is exited. Local variables are by default of automatic storage class. So the “auto” specifier is rarely used.

Storage Specifiers

=>register

When “register” specifier is placed before an automatic/local variable, it is to suggest the compiler to keep this variable in one of the computer’s high-speed registers in stead of memory. The loading and saving time is shorter than memory. By placing a heavily used variable into the register, the total run time can be reduced. However, the compiler may ignore this suggestion, if there is no register available. On the other hand, today’s optimizing compiler are capable of recognizing frequently used variables can decide to put them in register without the need for a register declaration.

Not often used.

=>static

If a local variable is declared “static”, it is still only known in the function in which they are defined, but when the function is exited, that variable is not destroyed, instead it is retained for next use.

Before the first time of use, if not specially initialized, all numeric variables are initialized to zero.

If there is an initialization in the function such as “static int x = 1;” it only works first time the function is called. Otherwise the variable can not keep the value of last time and therefore has no difference with normal local variables.

This kind of scope is mainly used by procedural languages. For OO it is rarely needed.

=>extern

Global variables (and function definitions) are created by placing variable declarations outside any function definition. Global variables default to storage class specifier “extern”. They keep their values throughout the execution of the program, and can be referred by any functions that follows their declarations or definitions in the file. According to PLP, the use of global variables should be avoided unless there is a special need.

1.17 Scope

=>File scope

A physical file is of file class. Therefore, global variables, function prototypes and function definitions, which are out of any function body, are of file scope.

=>Function scope

Because a function is not the smallest unit in C++, only labels are with function scope. Labels are identifiers followed by a colon, i. g., the case label “case 3.5: ” in “switch” structure. Labels are used in “switch” structure or “goto” statement to mark the location of a statement, so that other statement knows where to go. They are implementation details that functions hide from one another.

=>Block scope

Because block is the smallest unit in C++, most variables/identifiers are of block scope. They are local variables declared in a block.

=>Function-prototype scope

Identifiers in the function-prototype declaration have function-prototype scope. The identifiers can be reused elsewhere without ambiguity. This is the lowest scope in C++.

1.18 Recursion

A recursive function is a function that calls itself either directly or through other function. There are such kind of problems that the direct solution can not be easily expressed, but the relationship between the problem and its simpler version is explicit. Thus the problem can be simplified repeatedly reduced to a smaller version, until it reaches the simplest case called “base case”, and finally becomes known. In short, recursion keeps producing simpler version of the original problem until the base case is revealed.

From logical point of view, recursion is invalid or impractical: you can not use an unknown solution to solve a problem. But in C++ recursion only means to make another copy of the function and call it again. So recursion in C++ is not real recursion. Therefore it is simple.

A smart use of recursion on other issues:

int main()
{
  int c;
  if( ( c = cin.get() ) != EOF)
  {
    main( );
    cout << c;
  }
 
  return 0;
}

=>Recursion & iteration

All recursive solutions can be substituted by iterations. Iteration solutions are better than recursion in respect to performance, because recursion produces a series of function calls and copies of variables, thus takes more overhead, which iteration can avoid. Recursion is only good when it can mirror the real-world problem more naturally, thus the program is easier to understand and debug.

=>Exponential complexity caused by recursive calls

If in a recursive function there are three recursive calls, and number of recursion layers is n, then the total of recursive calls will be 3n. This is called “exponent explosion”. Try to avoid this situation.

1.19 Recursion Exercise 1 - Binary Search of an Array

#include <iostream>
#include <math>
 
#include <stdlib>
 
void bisearch (const int number, 
               int &location, const int array[],
               int from, int to);
 
int main ()
{
  const int asize = 13;
  int location, temp;
  int student [asize] = {0,1,2,3,4,5,6,7,8,9,10,111,222};
 
  bisearch (7, location, student, 0, asize-1);
 
  if(location < 0)
    cout << "The number " << number << " is not in this array." << endl;
  else
    cout << "The "<< location << "th element contains your number "<< number << endl << endl;
 
  return 0;
}
 
void bisearch (int number, int &amp; location, int array [], int from, int to)
 
{
  //Base Case
  int middle = (from + to) / 2;
 
  if(array[middle] == number)
  {
    location = middle;
    return;
  }
 
  if( ( number > array [to]) || ( number < array [from] ) )
  {
    location = -1;
    return;
  }
 
  if(middle == from)
  if(array[to] == number)
  {
    location = to;
    return;
  }
  else
  {
    location = -1;
    return;
  }
 
  //Recursion
  if( number > array [middle])
    bisearch (number, location, array, middle, to);
  else
    bisearch (number, location, array, from, middle);
 
  return;
}

1.20 Recursion Exercise 2 - Towers of Hanoi

 
#include <iostream>
 
void hanoi (char, char, char, int);
 
int main ( )
{
  int n = 4;
  hanoi ('a', 'b', 'c', n);
  cin >> n;
  return 0;
}
 
void hanoi (char from, char via, char to, int n)
{
  if(n==1)
  {
    cout << from << " => " << to << endl;
    return;
  }
 
  n--;
  hanoi (from, to, via, n);
  cout << from << " => " << to << endl;
 
  hanoi (via, from, to, n);
 
  return;
}

1.21 Order of Evaluation on Operands

a = function1 (a, b) + function2 (c, d);

In C++ the order of evaluation of the two operands beside some operators such as “+” is indefinite. If the result of the calculation depends on the order of evaluation of the two operands i. e. the calling order of the two functions - it is a lousy design indeed - then the result will be indefinite.

1.22 Constant Variable

To put “const” qualifier in front of a parameter in both the function prototype and function definition is to tell the compiler that the function doesn’t modify the variable. A constant variable or pointer should be initialized when declared.

1.23 Principle of Least Privilege (PLP)

The principle of least privilege is to always assign least data accessing privilege to the program. In most cases it is achieved by using of qualifier “const”. “const” is used to pass variables and arrays to functions in which they should not be modified. It is also used to define a local variable that shouldn’t be changed. Any attempt to modify the constant variable will be checked out by compiler before the program is run. Using this principle to properly design software can greatly reduce debugging time and improper side effects, and can make a program easier to modify and maintain.

1.24 Inline Functions

Some functions are of quite small size, but quite frequently called. Compared with the function call overhead, the program size reduced by not repeatedly include the block of statements may be trivial. In these cases we put “inline” qualifier in front of the function definition to tell compiler to insert the body of the called function into the calling function to avoid a call.

When the inline function is changed, all functions that call it should be re-compiled.

Keyword inline is specially useful for wrapper classes.

1.25 Reference

There are two ways to pass arguments to called functions in many languages: “call-by-value” and “call-by-reference”.

=>call-by-value

When an argument is passed by value, only a copy of the argument is passed to the called function. The copy constructor of the passed object is called to make the copy, requiring extra overhead. The original values are untouched. This is good for data security, but bad for performance.

=>call-by-reference

A reference is declared by putting “&” in front of the identifier in the function header. A reference is not a variable. It is just an alias of a variable.

When we talk about a variable, it is actually the address of one memory cell used to hold different values. In machine language we would directly use the address to represent the variable, but in high-level languages identifiers are used to present addresses. So when we write

int a = 33;

we actually created an alias to represent the address of a memory cell holding the value of 33. When compiler sees “a”, it just converts it to the corresponding address.

Therefore, when we write

int &b = a;

we just created another alias for that address. Now for the same address we have two alias: “a” and “b”. Although “b” is created “out of” a, but once created they are equal.

Therefore, when a object is passed by reference, a new alias is created in the called function to refer to the address of that object. With this alias the called object can do anything directly to the object itself.

void double(int &x)  // function definition indicating the reference
{  
  x * = 2; 
}
 
int main ( )
{
  int a = 3;
  double(a);
}

=>Comparison between call-by-value, call-by-reference and call-by reference with pointer

  Call-by-value Call-by-reference Call-by-reference with pointer
call sum(a, b); sum(a, b); sum(&a, &b);
prototype int sum(int a, int b) int sum(int &a, int &b) int sum(int * ptr1, int * Ptr2)

From the above form you can see that the most explicit expression is call-by-reference with pointer. The calling statement of call-by-reference is the same as call-by-value, therefore the programmer may forget that he is calling by reference.

1.26 Default Arguments

When the arguments you pass to the called function are most probably of some definite values, you can specify these values in the function prototype. When the values of the parameters are the default ones, you can omit the parameters.

void count(int x = 1, int y = 1, int z = 1)
{...}
 
int main ( )
{
  count();
  count (2, 3);
}

Only the rightmost arguments can be omitted — you can not provide the 1st and 3rd argument and omit the middle one.

1.27 Overloading Functions

Functions with the same name but different signature (i.e. argument list) are called overloaded functions. Overloaded functions are recognized by the compiler through the argument list in the call:

#include <iostream>
 
void print(int i)
{
  cout << "int i = " << i << endl;
}
 
void print(char c)
{
  cout << "char c = " << c << endl;
}
 
int main()
{
  int i = 1234;
  char c = 'C';
 
  print(i);
  print(c);
  cin << i;
}

1.28 EOF

An integer constant defined in the “iostream.h” header file. It is a system-dependent keystroke combination. For example, in MS-DOS it is “Ctrl-Z”, while in UNIX it is “Ctrl-D”. In other system it may be “EOF” or even “Stop here!”. The value of EOF in ANSI standard is a negative integer value, normally -1. In Borland C++ it is also -1.

1.29 New Line

A new line is regarded as a character in C++: “\n”. You can use such statement to detect a Return:

if(c = = ‘\n’)…

Popularity: 21%

You need to log on to convert this article into PDF


Related Blog Items

1 Comment

Leave a comment

*
To prove you're a person (not a spam script), type the security word shown in the picture.
Anti-spam image