Topic: Programming/ C++.

4th story: A tour of C++

I. Intro:

C++ is a general-purpose, case-sensitive, free-form programming language that supports procedural, object-oriented, and generic programming.

C++ is regarded as a middle-level language, as it comprises a combination of both high-level and low-level language features.

C++ was developed by Bjarne Stroustrup of AT&T Bell Laboratories in the early 1980's, and is based on the C language. The “++” is a syntactic construct used in C (to increment a variable), and C++ is intended as an incremental improvement of C. Most of C is a subset of C++, so that most C programs can be compiled (i.e. converted into a series of low-level instructions that the computer can execute directly) using a C++ compiler.

C++ is a superset of C, and that virtually any legal C program is a legal C++ program.

Advantages:

  1. vendor-neutral: the C++ standard is the same in any platform or compiler
  2. industrial (as opposed to academic): evolved to satisfy the needs of software engineers, not computer scientists
  3. efficient. Compiles into highly optimized CPU-specific machine code with little or no runtime overhead.
  4. multi-paradigm: allows the use and penalty-free mixing of procedural, OOP, generic programming, functional programming, etc
  5. strictly statically typed (unlike Python for example): a large amount of logic (and sometimes even calculations) can be proved and performed at compile time, by the type checking/inferring system.
  6. has deterministic memory management (as opposed to Java, C#, and other languages with garbage collectors): the life time of every object is known with absolute precision, which makes destructors useful and RAII possible.

Disadvantages:

  1. very complex! The learning curve is steep and takes a long time to climb, especially for those who know C or C# or other superficially similar languages
  2. has the concept of “undefined behavior” (just like C) – a large class of errors that neither compiler nor the runtime system is required to diagnose.
  3. has some design flaws, although they are largely fixed by boost libraries and the new language standard.

II. Variables & Operators:

A variable in C++ is a name for a piece of memory that can be used to store information.

There are many types of variables, which determines the size and layout of the variable’s memory;

Variable Names:

1. Variables:

we can use any combination of letters and numbers for Variable and function names but it must start with a letter.

We can use Underscore (_) as a letter in variable name and can begin with an underscore But Identifiers beginning with an underscore are reserved, And identifiers beginning with an underscore followed by a lower case letter are reserved for file scope identifiers Therefore using underscore as starting letter is not desirable.

Akki and akki are different identifiers because upper and lower case letters are treated as different identifiers

Variable Types:

There are many ‘built-in’ data types in C:

char 0 to 255 or -128 to +127 (1 byte)

unsigned char 0 to 255 (1 byte)

signed char -128 to 127 (1 byte)

short -32,768 to +32,767 (2bytes)

unsigned short 0 to +65,535 (2 bytes)

int -2,147,483,648 to +2,147,483,647 (4 bytes)

unsigned int 0 to 4,294,967,295 (4 bytes)

float: single precision floating point (4 bytes)

double: double precision floating point (8 bytes)

long double extended precision floating point (10 bytes)

Definition, Declaration & Initialization:

Definition is the place where variable is created (allocated storage).

Declaration is a place where nature (type) of variable is stated, but no storage is allocated.

Initialization means assigning a value to the variable.

Variables can be declared many times, but defined only once. Memory space is not allocated for a variable while declaration. It happens only on variable definition.

Variable declaration:

syntax: data_type variable_name;

example

int a, b, c;

char flag;

Variable initialization:

syntax

data_type variable_name = value;

example

int a = 50;

char flag = ‘t’;

external and static, auto and register :

initialisation done each time block is entered.

external and static variables cannot be initialised with a value that is not known until run-time; the initialiser must be a constant expression.

A variable that has not been assigned a value is called an uninitialized variable. Uninitialized variables are very dangerous because they cause intermittent problems (due to having different values each time you run the program). This can make them very hard to debug.

2. Operators:

Arithmetic Operators

  • multiplication
  • / division
  • % remainder after division (modulo arithmetic)
  • + addition
  • - subtraction and unary minus

Logical Operators

&& Called Logical AND operator. If both the operands are non-zero, then condition becomes true.

|| Called Logical OR Operator. If any of the two operands is non-zero, then condition becomes true.

! Called Logical NOT Operator. Use to reverses the logical state of its operand. If a condition is true, then Logical NOT operator will make false.

Increment and Decrement Operators

Increment and decrement operators are used to add or subtract 1 from the current value of oprand.

++ increment

– – decrement

Increment and Decrement operators can be prefix or postfix. In the prefix style the value of oprand is changed before the result of expression and in the postfix style the variable is modified after result.

For eg.

a = 9;

b = a++ + 5; /* a=10 b=14 */

a = 9;

b = ++a + 5; /* a=10 b=15 */

Relational Operators

== equal.

!= Not equal.

> < Greater than/less than

>= greater than or equal to

<= less than or equal to

Cast Operators:

Cast operators are used to convert a value from one to another type.

(float) sum; converts type to float

(int) fred; converts type to int

Bitwise Operators:

Bitwise operators performs operation on actual bits present in each byte of a variable. Each byte contain 8 bits, each bit can store the value 0 or 1

~ one’s complement

& bitwise AND

^ bitwise XOR

| bitwise OR

<< left shift (binary multiply by 2)

>> right shift (binary divide by 2)

Assignment Operators:

= assign

+= assign with add

-= assign with subtract

*= assign with multiply

/= assign with divide

%= assign with remainder

>>= assign with right shift

<<= assign with left shift

&= assign with bitwise AND

^= assign with bitwise XOR

|= assign with bitwise OR

For example,

a = a + 64; is same as

a += 64;

sizeof:

The sizeof operator returns the size, in bytes, of a type or a variable.

You can compile and run the following program to find out how large your data types are:

cout << “bool:\t\t” << sizeof(bool) << “ bytes”;

We cannot really print this because is implementation defined.

Condition ? X : Y:

Condition operator: If Condition is true ? then it returns value X : otherwise value Y

Comma ‘,’:

Comma operator causes a sequence of operations to be performed. The value of the entire comma expression is the value of the last expression of the comma-separated list

. (dot) and -> (arrow)

Member operators are used to reference individual members of classes, structures, and unions.

& Pointer operator: & returns the address of an variable. For example &a; will give actual address of the variable.

  • Pointer operator: * is pointer to a variable. For example *var; will pointer to a variable var.

III. Functions:

1. In-line functions:

Calling a function generally causes a certain overhead (stacking arguments, jumps, etc…), and thus for very short functions, it may be more efficient to simply insert the code of the function where it is called, instead of performing the process of formally calling a function.

Preceding a function declaration with the inline specifier informs the compiler that inline expansion is preferred over the usual function call mechanism for a specific function. This does not change at all the behavior of a function, but is merely used to suggest the compiler that the code generated by the function body shall be inserted at each point the function is called, instead of being invoked with a regular function call.

For example, the concatenate function above may be declared inline as:

inline string concatenate (const string& a, const string& b)

{

return a+b;

}

This informs the compiler that when concatenate is called, the program prefers the function to be expanded inline, instead of performing a regular call. inline is only specified in the function declaration, not when it is called.

Note that most compilers already optimize code to generate inline functions when they see an opportunity to improve efficiency, even if not explicitly marked with the inline specifier. Therefore, this specifier merely indicates the compiler that inline is preferred for this function, although the compiler is free to not inline it, and optimize otherwise. In C++, optimization is a task delegated to the compiler, which is free to generate any code for as long as the resulting behavior is the one specified by the code.

2. Recursion:

Recursion is a programming technique that allows the programmer to express operations in terms of themselves.

In C++, this takes the form of a function that calls itself. A useful way to think of recursive functions is to imagine them as a process being performed where one of the instructions is to “repeat the process”. This makes it sound very similar to a loop because it repeats the same code, and in some ways it is similar to looping. On the other hand, recursion makes it easier to express ideas in which the result of the recursive call is necessary to complete the task. Of course, it must be possible for the “process” to sometimes be completed without the recursive call.

void CountDown(int nValue)

{

using namespace std;

cout << nValue << endl;

CountDown(nValue-1);

}

int main(void)

{

CountDown(10);

return 0;

}

When CountDown(10) is called, the number 10 is printed, and CountDown(9) is called. CountDown(9) prints 9 and calls CountDown(8). CountDown(8) prints 8 and calls CountDown(7). The sequence of CountDown(n) calling CountDown(n-1) is continually repeated, effectively forming the recursive equivalent of an infinite loop.

This program illustrates the most important point about recursive functions: you must include a termination condition, or they will run “forever” (or until the call stack runs out of memory).

Stopping a recursive function generally involves using an if statement. Here is our function redesigned with a termination condition:

void CountDown(int nValue)

{

using namespace std;

cout << nValue << endl;

// termination condition

if (nValue > 0)

CountDown(nValue-1);

}

int main(void)

{

CountDown(10);

return 0;

}

Now when we run our program, CountDown() will count down to 0 and then stop!

IV. Object-Oriented Approach:

1.C-TOR & DC-TOR:

Constructor:

A constructor is a special kind of class member function that is executed when an object of that class is instantiated.

Constructors are typically used to initialize member variables of the class to appropriate default values, or to allow the user to easily initialize those member variables to whatever values are desired.

Unlike normal functions, constructors have specific rules for how they must be named:

1) Constructors should always have the same name as the class (with the same capitalization)

2) Constructors have no return type (not even void)

A constructor that takes no parameters (or has all optional parameters) is called a default constructor but if you need, a constructor can have parameters. This helps you to assign initial value to an object at the time of its creation.

Destructor:

A destructor is another special kind of class member function that is executed when an object of that class is destroyed. They are the counterpart to constructors. When a variable goes out of scope, or a dynamically allocated variable is explicitly deleted using the delete keyword, the class destructor is called (if it exists) to help clean up the class before it is removed from memory.

For simple classes, a destructor is not needed because C++ will automatically clean up the memory for you. However, if you have dynamically allocated memory, or if you need to do some kind of maintenance before the class is destroyed (eg. closing a file), the destructor is the perfect place to do so.

Like constructors, destructors have specific naming rules:

1) The destructor must have the same name as the class, preceded by a tilde (~).

2) The destructor can not take arguments.

3) The destructor has no return type.

Note that rule 2 implies that only one destructor may exist per class, as there is no way to overload destructors since they can not be differentiated from each other based on arguments.

2. Inheritance:

Inheritance is one of the key feature of object-oriented programming including C++ which allows user to create a new class(derived class) from a existing class(base class). The derived class inherits all feature from a base class and it can have additional features of its own.

Inheritance makes the code reusable. When we inherit an existing class, all its methods and fields become available in the new class, hence code is reused.

Note that All members of a class except Private, are inherited.

3. Polymorphism & Virtual functions:

Polymorphism:

“Poly” means “many” and “morph” means “form”. Polymorphism is the ability of an object (or reference) to assume (be replaced by) or become many different forms of object. Typically, polymorphism occurs when there is a hierarchy of classes and they are related by inheritance.

C++ polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that invokes the function. Example: function overloading, virtual functions. Another example can be a plus + sign, used for adding two integers or for using it to concatenate two strings.

Virtual Function:

A virtual function is a function in a base class that is declared using the keyword virtual. Defining in a base class a virtual function, with another version in a derived class, signals to the compiler that we don’t want static linkage for this function.

What we do want is the selection of the function to be called at any given point in the program to be based on the kind of object for which it is called. This sort of operation is referred to as dynamic linkage, or late binding.

4. Data abstraction:

Object Oriented Programming has a special feature called data abstraction. Data abstraction allows ignoring the details of how a data type is represented. While defining a class, both member data and member functions are described. However while using an object (that is an instance of a class) the built in data types and the members in the class are ignored. This is known as data abstraction. This can be seen from the above example.

Benefits of Data Abstraction

  • Class internals are protected from inadvertent user-level errors, which might corrupt the state of the object.
  • The class implementation may evolve over time in response to changing requirements or bug reports without requiring change in user-level code.

5. Data encapsulation:

Encapsulation is the method of combining the data and functions inside a class. This hides the data from being accessed from outside a class directly, only through the functions inside the class is able to access the information.

This is also known as “Data Abstraction”, as it gives a clear separation between properties of data type and the associated implementation details. There are two types, they are “function abstraction” and “data abstraction”. Functions that can be used without knowing how its implemented is function abstraction. Data abstraction is using data without knowing how the data is stored.

C++ supports the properties of encapsulation and data hiding through the creation of user-defined types, called classes. We know that class can have private, protected and public members. By default, all items defined in a class are private. For example: class add { public: double getadd(void) { return no1 + no2; } private: double no1; double no2; }; The variables no1 & no2 are private. This means that they can be accessed only by other members of the add class, and not by any other part of your program. This is one way encapsulation is achieved.

V. Files:

A file is collection of related records, a record is composed of several fields and field is a group of character.

This requires another standard C++ library called fstream which defines three new data types:

  • Ofstream: This data type represents the output file stream and is used to create files and to write information to files.
  • Ifstream: This data type represents the input file stream and is used to read information from files.
  • Fstream: This data type represents the file stream generally, and has the capabilities of both ofstream and ifstream which means it can create files, write information to files, and read information from files.

VI. Advanced concepts:

1. Exception handling:

Exceptions provide a way to react to exceptional circumstances (like runtime errors) in our program by transferring control to special functions called handlers.

C++ exception handling is built upon three keywords: try, catch, and throw.

throw: A program throws an exception when a problem shows up. This is done using a throw keyword.

catch: A program catches an exception with an exception handler at the place in a program where you want to handle the problem. The catch keyword indicates the catching of an exception.

try: A try block identifies a block of code for which particular exceptions will be activated. It’s followed by one or more catch blocks.

Assuming a block will raise an exception, a method catches an exception using a combination of the try and catch keywords. A try/catch block is placed around the code that might generate an exception. Code within a try/catch block is referred to as protected code.

2. Memory allocation:

Allocating memory

There are two ways that memory gets allocated for data storage:

Compile Time (or static) Allocation

  • Memory for named variables is allocated by the compiler
  • Exact size and type of storage must be known at compile time
  • For standard array declarations, this is why the size has to be constant

Dynamic Memory Allocation

  • Memory allocated “on the fly” during run time
  • dynamically allocated space usually placed in a program segment known as the heap or the free store.
  • Exact amount of space or number of items does not have to be known by the compiler in advance.
  • For dynamic memory allocation, pointers are crucial.

Dynamic Memory Allocation

We can dynamically allocate storage space while the program is running, but we cannot create new variable names “on the fly”

For this reason, dynamic allocation requires two steps:

  • Creating the dynamic space.
  • Storing its address in a pointer (so that the space can be accesed)

To dynamically allocate memory in C++, we use the new operator.

De-allocation:

  • Deallocation is the “clean-up” of space being used for variables or other data storage
  • Compile time variables are automatically deallocated based on their known extent (this is the same as scope for “automatic” variables)
  • It is the programmer’s job to deallocate dynamically created space
  • To de-allocate dynamic memory, we use the delete operator

Allocating space with new

To allocate space dynamically, use the unary operator new, followed by the type being allocated.

new int;

// dynamically allocates an int

new double;

// dynamically allocates a double

If creating an array dynamically, use the same form, but put brackets with a size after the type:

new int[40];

// dynamically allocates an array of 40 ints

new double[size];

// dynamically allocates an array of size doubles

These statements above are not very useful by themselves, because the allocated spaces have no names! BUT, the new operator returns the starting address of the allocated space, and this address can be stored in a pointer:

int * p;

// declare a pointer p

p = new int;

// dynamically allocate an int and load address into p

Deallocation of dynamic memory

To deallocate memory that was created with new, we use the unary operator delete. The one operand should be a pointer that stores the address of the space to be deallocated:

int * ptr = new int;

// dynamically created int

delete ptr;

// deletes the space that ptr points to

3. Templates:

Templates in C++ programming allows function or class to work on more than one data type at once without writing different codes for different data types. Templates are often used in larger programs for the purpose of code reusability and flexibility of program. The concept of templetes can be used in two different ways:

  • Function Template
  • Class Template

Function Template

A function templates work in similar manner as function but with one key difference. A single function template can work on different types at once but, different functions are needed to perform identical task on different data types.

C++ gives us a lot of advanced topic, like the STL which will be explained in an other story! And with the new std C++20, the features go more and more advanced. We will discover all that in next stories.

Hope you had fun! See you soon :)

C++ | STL | Semiconductors | VHDL | SoCs | FPGAs | Python | Visual Studio | sometimes Go