C++ was created in the early 80's by Bjarne Stroustrup at Bell Labs. It is a general purpose object oriented programming language used in a variety of different application. Let us study how a computer program is executed on a computer. The diagram is somewhat simplistic and offers an overview of how a computer will work in general.
The instructions for the program are stored in the RAM and the CPU grabs an instruction from the RAM and executes it. It will then grab the next instruction and execute that and so on. What type of instructions are these ? Well these are the instructions that the CPU understands. In case of an Intel CPU the machine language is based on x86 . That is the only thing the CPU understands. It does not know about C++ , Java or any other language. It only knows about the x86 machine language. What do the instructions of this machine language look like ?
000000 00001 00010 00110 00000 100000
100011 00011 01000 00000 00001 000100
As you can imagine writing machine language instructions is a tedious and impractical task. Programmers
used Assembly language instead to do their programming. So how did the CPU run Assembly language .
Well it didn't. The Assembly language program was converted to machine language by a compiler.
What did the Assembly language instructions look like ?
MOV AL, 1h ; Load AL with immediate value 1
MOV CL, 2h ; Load CL with immediate value 2
MOV DL, 3h ; Load DL with immediate value 3
The Assembly language instruction had one to one correlation with machine language and was
slightly better in terms of using opcodes but still a tedious task. So now the high level languages
came such as Fortran, Pascal, C . For a long time programming was done in these high level
languages . These language had features like functions, procedures , structures. A programmer could
use variables and did not have to worry about mapping variables to specific addresses in memory.
However these languages involved the programmer converting the application problem in
terms of the language which still bore a close relationship to the way the CPU worked. The
concept of object oriented languages was developed and thus came languages such as Java,
C# and C++ . Now a programmer could translate the application problem in terms of objects and
classes. The way we think about problems and concepts can be correlated and organized better with
object oriented programming. As an example think of an application for the bank . We can think of
a customer with properties such as name, age, gender, address, phone. This customer could have an
account and the account could be checking or saving. An account can have properties like amount and
account number. It could also have methods like add a deposit and so on. This sort of technique can
be used in many problems where we can model the solution in terms of classes to more closely resemble
the problem domain.
C++ is based on the "C" language that was created by Dennis Ritchie in the late 60's. The "C"
language was a procedural based language . Object oriented features were added to it and C++
was born. C++ is a super set of the "C" language and most "C" programs can run under "C++". Both
the languages involve a steep learning curve.
We are going to write our first program, compile and run it under different operating systems. As is the convention our first program is going to print "Hello World" to the console and that's all it does.
On the Windows operating system we have many options in order to compile and run it.
Cygwin
One is to install the Cygwin software from the site:
Choose the "g++" compiler and the "make" utility from the options when installing. For a text editor we have many options available . One editor is "Textpad" that can be downloaded from the site:
You should also download the syntax definition file from the "Textpad" site for C++ language. That will enable the syntax coloring .
Now the "Hello World" program:
cout << "Hello World." << endl ;
Save this to a file. You can name it to be "hello.cpp" but it can be anything you like. Then from the "Cygwin" command line, navigate to the folder where you created "hello.cpp" and type in:
g++ hello.cpp
Then do a "ls" and you should see a file "a.exe" or "a.out" . This is the executable that got created.
We can run this executable by the command :
./a.out
and you should see the following output on the console:
Hello World
Eclipse and Netbeans
Other IDE environments that can be installed are Eclipse and Netbeans but these require a compiler such as "g++" to be installed separately. Microsoft has an IDE called "Visual Studio Express" that can be downloaded for free. There are some restrictions on this particular version but it can be used for learning purposes.
Microsoft Visual Studio
Microsoft has Visual Studio that is a tool that can be used to compile different languages including C++ . They are supposed to have a free edition of the the Visual Studio.
Hills.ccsf.edu
We can also use our "hills.ccsf.edu" remote unix system . This is what we will be using in class and you must do your assignments on this system. The software to access and use this can be any SSH based client ftp and "Putty" type command line system.
Winscp
Putty
https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
The program "putty.exe" is a single file that be saved to a folder such as "C:\putty" . We can integrate Putty with WinScp by
changing the "Option/Preferences" screen .
The checkbox "Remember session password will allow you to open up Putty from WinScp without a prompt for password.
On the Mac there is software by Apple called Xcode . You need an Apple developer account and it's free. There are 2 versions of it. One includes an IDE and the other is command line based. If you do not want to install "xcode" on the Mac then you can use the Hills from the "Terminal" window.
[amittal@fog ~]$ ssh amittal@hills.ccsf.edu
Enter your password and now you are in the Hills system. To create your files you can use the "vi", "vim" or "nano" editor. You can read about these editors on the web. You can also use "sftp" to transfer files from your Mac to the Hills server if you want through the Terminal window.
The web site:
https://codeanywhere.com
can also be used to connect to the hills server and you can do your assignments on this server.
Use the connections menu on the left hand side and choose the "SFTP" option and enter your settings as below with your username and password.
Unix systems vary in the setup of the C++ compiler. Usually one can check for the existence of the "g++" compiler by typing:
g++
on the command line. If you get an error of the sort "input file not found" the C++ compiler is installed and if you get "command not found" error then the g++ compiler is not installed.
Compilation
We have seen that if we have a C++ program:
File: "hello.cpp"
cout << "Hello World." << endl ;
we can compile it with the following command:
g++ hello.cpp
In the above command we have not specified an executable so the C++ compiler produces the default executable with a name of "a.out" that we can run with the command.
./a.out
We need to place the "./" in front of the executable name to tell Unix to look for the executable in the current folder instead of in the environment path. We can specify the name of the executable using the "-o" option.
g++ -o hello hello.cpp
./hello
We can also compile to an executable using object files. Let's study that in the next section.
Object and Executable files
We can compile to an object format using the "-c" option.
g++ -c hello.cpp
This will produce a file named "hello.o" . This is an object file containing machine language instructions. However it is not an executable. We cannot run it.
[amittal@hills object_1]$ ./hello.o
-bash: ./hello.o: Permission denied
Even if we change the permissions on the file
chmod 777 hello.o
and try to run it again we get:
[amittal@hills object_1]$ ./hello.o
-bash: ./hello.o: cannot execute binary file
The compiler does not check if the file has a main method or if it has function definitions. The compiler makes sure that the syntax is correct and then compiles to machine language instructions.
File: "hello1.cpp"
#include <iostream>
using namespace std ;
int main1()
{
cout << "Hello World." << endl ;
return(0) ;
}
[amittal@hills object_1]$ g++ -c hello1.cpp
[amittal@hills object_1]$ g++ hello1.o
/lib/../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
After compiling if we try to link the object file and create an executable file then the C++ compiler complains about not finding the "main" method.
File: "hello2.cpp"
#include <iostream>
using namespace std ;
int add( int num1, int num2 ) ;
int main()
{
add( 3 , 4 ) ;
cout << "Hello World." << endl ;
return(0) ;
}
[amittal@hills object_1]$ g++ -c hello2.cpp
[amittal@hills object_1]$ g++ hello2.o
hello2.o: In function `main':
hello2.cpp:(.text+0xf): undefined reference to `add(int, int)'
collect2: error: ld returned 1 exit status
[amittal@hills object_1]$
We have declared a function "add" but not provided a definition for it. We are able to compile and produce an object file for it because the rule for a file compilation in C++ is that the file only needs a declaration in order for the file to get compiled . However when we try to create the executable then the compiler will check if the definition exists somewhere and if it doesn't then it will complain.
Both object files and executable files contain machine language code but an object file does not need a "main" method and it does not need a definition of a function.
Make Utility
Once you are on the Unix system a utility called "make" can also be used to compile your program. We can create entries in a text file called "Makefile" . As an example to compile the "hello1.cpp" program we can use:
File: "hello1.cpp"
cout << "Hello World." << endl ;
File: Makefile
hello.exe: hello1.cpp
g++ -o hello.exe hello1.cpp
The first item on the line "hello.exe" is the target. That is the file we want to end up with. To the right of that we have the dependency which is "hello1.cpp" and below that we have the command to build the target. The second line should have tabs before it. The "make" utility is very particular about the tabs and will not accept spaces. So what's the advantage of the "makefile" ? Well for one you do not have to type the whole command in. You can just type:
make
and that will get the compilation process going.
We can also write the Makefile so that it works with object files instead. What's an object file ? We can direct the C++ compiler to compile to an object file instead of an executable with the following command:
g++ -c hello.cpp
This produces a file called "hello.o" . This is also a machine language file but is not an executable. We can't run it. The "-c" option does not force the compiler to look for a "main" method. To produce an executable we run the "g++" command again but use an object file instead of a ".cpp" file.
g++ hello.o
This will then produce an executable that we can actually run. Why do we need this object file approach to begin with. Can't we just compile the ".cpp" files every time ? Compiling a single file may not make much of a difference but consider a large project consisting of many files. Let's say our project consists of 5 files and we compile an executable using the command:
g++ first1.cpp first2.cpp first3.cpp first4.cpp first5.cpp
Now if we change the "first1.cpp" then using the above command all the files must be compiled again. That means the compiler has to check the grammar and syntax errors and then compile each file to machine code and then link them together to produce the executable file. Using object files approach we can do:
g++ -c first1.cpp
g++ -c first2.cpp
...
g++ -c first5.cpp
This will produce the files:
first1.o first2.o first3.o first4.o first5.o
Then we can link them together to produce the executable file.
g++ first1.o first2.o first3.o first4.o first5.o
Let's see what happens if we change the code in first1.cpp . We do not need to recompile the rest of the files. We can compile the first1.cpp with the command:
g++ -c first1.cpp
and then link the files together:
g++ first1.o first2.o first3.o first4.o first5.o
The process of linking is very fast as the files have already been compiled to machine language code.
Let's see how we would do the above process using the make utility for 2 files:
first1.cpp and first2.cpp
exce1.exe: first1.o first2.o
g++ -o exec1.exe first1.o first2.o
first1.o: first1.cpp
g++ -c first1.cpp
first2.o: first2.cpp
g++ -c first2.cpp
The dependency can be a target as in the above case. The dependencies will be evaluated and if the "make" utility determines that there has been a change in the dependency then it will run the command to create "exec1.exe" .
A C++ program must have the "main" function. This is where the execution starts. It can be composed of a single file or multiple files. Usually the extension of the file is "*.cpp" and the extension of the header file is "*.h" . The header files are not used in the compilation step but in the pre processor step. Just like other languages there is a grammar and rules for writing C++ code. We have keywords, variable names, functions. If a C++ program has syntax errors then the compiler will throw out errors.
File: "hello.cpp"
#include <iostream>
using namespace std ;
int main()
{
cout << "Hello World." << endl ;
}
Every C++ program must have a main method. That is where the execution starts from. Any functions or variables that are used from the library must be declared. That is where the "include" files come in.
Let's say we compile the above program on Windows and we get an exe named "hello.exe" . Can we take this "exe" and run it say on a Mac or Windows machine ? No we can't. Even if the processors are the same ( Intel compatible ) an operating system has it's own format for executable s . Specific operating system information is inserted into the executable making it impossible to run on other systems.
Namespaces are used to prevent name conflicts between variables, functions. A namespace can be thought of as enclosing a set of variables or function names into a block that has a label. This is especially important when global variables come into play. Let's say we have the following program "namespace.cpp" :
#include <iostream>
using namespace std ;
//Global variable
int var1 = 100 ;
int main()
{
//local variable hides the global variable
int var1 = 5 ;
cout << var1 << endl ;
}
Output:
5
One way of accessing the global variable ( same technique can be used for functions ) is to enclose the variable in a namespace.
#include <iostream>
using namespace std ;
namespace group1
{
int var1 = 100 ;
}
int main()
{
int var1 ;
var1 = 5 ;
cout << group1::var1 << endl ;
}
Output:
100
Using namespace for functions.
In the preprocessor section we create 3 files:
main.cpp , mymath1.cpp , mymath1.h
Let's say we used the following code in mymath1.cpp":
#include "mymath1.h"
namespace mine
{
int doSum( int x1, int x2 )
{
return ( x1 + x2 ) ;
}
}
and the contents of "main.cpp" are:
#include "mymath1.h"
#include <iostream>
using namespace std ;
using namespace mine ;
int main()
{
int result = doSum( 4, 5 ) ;
cout << "Hello World" << endl ;
return 1 ;
}
and the contents of "mymath1.h: are:
int doSum( int x1, int x2 ) ;
However if we try to compile the code we will see an error:
undefined reference to `doSum(int, int)'
We need to modify the contents of the header file "mymath1.h" to include the namespace also.
"mymath1.h"
namespace mine
{
int doSum( int x1, int x2 ) ;
}
The Unix g++ compiler also has a debugger facility. Let's take the below program and use the "gdb" debugger with it.
"main.cpp"
#include <iostream>
using namespace std ;
int main()
{
int i1 = 5 ;
i1 = i1 + 2 ;
i1 = i1 + 3 ;
i1 = i1 + 4 ;
return 1 ;
}
To work with the debugger we need to use different compiler option.
g++ -g main.cpp
and to run the gdb:
gdb ./a.out
We can then set break points:
break 11
The command to step through is:
step
The command to run is:
run
We can also print values with the print command:
print i1
The debugger in an IDE will be much easier to use in terms of the graphical interface than the gdb. However gdb provides the functionality that one expects from a normal debugger.
We have 2 files:
"main.cpp"
int main()
{
g1 = 5 ;
}
and
"mymath1.cpp"
//Global Variable
int g1 ;
We know that if we try to compile the 2 files with the command:
main.cpp:7:2: error: ‘g1’ was not declared in this scope
We receive an error of the form:
main.cpp:7:2: error: ‘g1’ was not declared in this scope
We can't use the same technique that we used for the functions because if we do:
"main.cpp"
int g1 ;
int main()
{
g1 = 5 ;
}
Then we are defining the variable "g1" again . We need some way to declare but not define the "g1" variable. This is where the "extern" type comes in. Now our "main.cpp" becomes:
"main.cpp"
extern int g1 ;
int main()
{
g1 = 5 ;
}
This tells the "main.cpp" that the variable "g1" is defined somewhere else in the project.
The C++ compiler compiles a ".cpp" file to machine code. This process consists of 2 parts. The first is where the preprocessor part of compiler modifies your ".cpp" file and the second part is where it converts the modified file to machine code. All the preprocessor statements start with the hash "#" character. Lets take an example of how this works. We can have our program spread over multiple files. Let's take an example of 2 files mymath1.h and mymath1.cpp . In the file "mymath1.cpp" I have the code:
"mymath1.cpp"
int doSum( int x1, int x2 )
{
return ( x1 + x2 ) ;
}
Also I have the file "main.cpp" that has the following code:
"main.cpp"
#include <iostream>
using namespace std ;
int main()
{
int result = doSum( 4, 5 ) ;
cout << "Hello World" << endl ;
return 1 ;
}
Now we compile the 2 files to create an executable:
g++ -o main.exe main.cpp mymath1.cpp
However running the above command produces the error:
main.cpp: In function ‘int main()’:
main.cpp:11:28: error: ‘doSum’ was not declared in this scope
int result = doSum( 4, 5 ) ;
This error is happening because we need to tell "main.cpp" the signature of the function. What are the parameters that "doSum" takes and what value does it return ? This is called a "declaration" . Adding this declaration produces the following code:
#include <iostream>
using namespace std ;
int doSum( int x1, int x2 ) ;
int main()
{
int result = doSum( 4, 5 ) ;
cout << result << endl ;
return 1 ;
}
Now the compilation succeeds:
g++ -o main.exe main.cpp mymath1.cpp
The file "main.cpp" is compiled first and the compiler makes sure that the function "doSum" is used properly. It does not have the full definition of the function yet because that is in another file "mymath1.cpp" . The compiler will create an object file internally for "main.cpp" . Right now it has not verified that the "doSum" has actually a definition but it will do that later when linking. Now it compiles "mymath1.cpp" to an object file. Now there are 2 object files and the linking process begins. The process of linking takes the 2 object files and will check if the declared functions have the definitions for them and also whether the function "main" exists. It will then create an executable.
What if some other file in our project wanted to use the "doSum" function? We would have to include the declaration for the function. We can do this in a better way by using header files. We can place the declaration in the header file and name the header file "mymath.h" .
Now the three files look like:
"mymath.cpp"
int doSum( int x1, int x2 )
{
return ( x1 + x2 ) ;
}
"mymath.h"
int doSum( int x1, int x2 ) ;
"main.cpp"
#include <iostream>
#include "mymath.h"
using namespace std ;
int main()
{
int result = doSum( 4, 5 ) ;
cout << result << endl ;
return 0 ;
}
Any statements that begin with the "hash" character represent "preprocessor" statements . When the preprocessor sees the "#include "mymath1.h" it takes the contents of the header file "mymath1.h" and pastes them in the file "main.cpp" . This has the same effect of placing the declaration in the file "main.cpp" . A header file is not used in the compilation command. The command is still:
g++ -o main.exe main.cpp mymath1.cpp
The header file is processed by the preprocessor and a new "main.cpp" file is created that contains the contents of the header file.
We work with numbers using base 10 or the decimal system. When we have a number such as
245
This is interpreted as :
2 * 10 to power 2 + 4 * 10 to power 1 + 5
If we have a base such as 10 then the digits range from 0 to 9 ( 0 to base -1 ) . When we go from right to left we
multiply the digit by the base to the power increasing the power by 1 each time.
Computer work with base 2 also called the binary system. The digits are 0 and 1 . The number
101
is 5 in decimal system( 1*2 power2 + 0 + 1 ) .
If we have 3 digits then the unsigned number will look like below:
000 -- 0
001 -- 1
010 -- 2
011 -- 3
100 -- 4
101 -- 5
110 -- 6
111 -- 7
Two's complement.
A negative binary number is represented in two's complement. In this scheme the leftmost bit is "1" and represents the negative number. The right most bits are inverted and then a "1" is added to get the negative number amount.
Ex:
101
The bit "01" are inverted to form "10" and then a "1" is added to form "11" so the number "101" is actually "-3" . If we have 3 digits then the signed numbers look as below:
000 0
001 1
010 2
011 3
100 -4
101 -3
110 -2
111 -1
Base 16 also called Hexadecimal system . The digits are 0-9, A-F with A representing 10 and F representing 15.
Examples:
F0 - 240
AF - 175
A valid variable must have as the first character the letter a to z or A to Z or the underscore character "_" . After the first character; a character can be a to z or A to Z or a digit from 0 - 9 or the underscore. In C++ uppercase and lowercase characters represent unique names and can't be interchanged. The name "score" is different from the name "Score" .
Below are the primitive data types:
Integer
Character
Boolean
Floating Point
Double
Void
Wide Char
A data type can have additional modifiers. As an example the "Integer" data type can have the following modifiers:
Signed
Unsigned
Short
Long
A program that prints the size of different types.
#include<iostream>
using namespace std;
int main()
{
cout << "Size of char : " << sizeof(char) << " byte" << endl;
cout << "Size of int : " << sizeof(int) << " bytes" << endl;
cout << "Size of short int : " << sizeof(short int) << " bytes" << endl;
cout << "Size of long int : " << sizeof(long int) << " bytes" << endl;
cout << "Size of signed long int : " << sizeof(signed long int) << " bytes" << endl;
cout << "Size of unsigned long int : " << sizeof(unsigned long int) << " bytes" << endl;
cout << "Size of float : " << sizeof(float) << " bytes" <<endl;
cout << "Size of double : " << sizeof(double) << " bytes" << endl;
cout << "Size of wchar_t : " << sizeof(wchar_t) << " bytes" <<endl;
return 0;
}
Output:
Size of char : 1 byte
Size of int : 4 bytes
Size of short int : 2 bytes
Size of long int : 8 bytes
Size of signed long int : 8 bytes
Size of unsigned long int : 8 bytes
Size of float : 4 bytes
Size of double : 8 bytes
Size of wchar_t : 4 bytes
We have the machine language and then assembly language and after that the high level languages. We have the scripting languages that are part of this high level languages category. Languages like Dos Batch, JavaScript, Perl, Ruby, PHP and Unix Shell fall in this category. These languages are not compiled but simply run. We need to have the installed software that will go through the source code and run it. As an example if we have a Python script then we create a source file say "first.py" and we then run it with a Python interpreter. Scripting languages allow us to write and solve problems that would be time consuming with high level languages. They are used for a variety of tasks such as administrative , text processing, web gui .
C++98
C++03
C++11 Introduction of lambdas, auto, thread
C++14 Variable templates
C++17 Std variant, optional
C++20 Template syntax lambdas
The version numbers correspond to the year that the version was released. The newer versions are from C++ 11 onwards.
The "hills.ccsf.edu" is a unix server. When a utility such as "Putty" is used then we have a command line interface that we can use to compile and run our programs.
ls
The command "ls" will list the files and folders in our current directory.
Ex: ls
Ex: ls -l
ls with the "-l" option produces a detailed list of files.
pwd
The command "pwd" lists the current directory we are in ( Present Working Directory ) .
mkdir
The command "mkdir" can be used to create a folder. Ex:
mkdir demo
cd
Changes your current folder. Ex:
cd demo
cd .. //Goes one folder up
cd //Goes to the default home folder
cd ~ //Same thing changes to the default home folder
touch
The command "touch" is used to update the access date/time of a file. It can also be used to create an empty file. Ex:
touch hello.cpp
rm
Removes a file. Use rm with options to directory and all it's contents inside it.
Ex: rm "test1"
Ex: rm -rf someFolder
cp
Copies a file to some new file.
Ex:
cp hello1.cpp hello2.cpp
mv
Moves a file to another location; deleting the original file. Can also be used to rename the original file in the same folder.
mv hello1.cpp hello2.cpp
cat
The "cat" command can be used to print the contents of a file. It can also print contents of multiple files.
tar
The "tar" command can be used to compress and extract files. To compress the folder "mymath" to a file called "mymath.gz"
tar -czvf mymath.gz mymath
To extract the compressed file use:
tar -xvf mymath.gz
This will create a folder "mymath" in the current directory and extract all the files there.
chmod
A Unix file has permissions associated with it .There are permissions for reading, writing and executing the file. This 3 permissions can be set for 3 different sets of people: the owner ( creator fo the file ) , the users in the group that the owner belongs to and the third set everyone else.
The permissions for a file can be viewed with the command "ls -l" .
[amittal@hills unix_stuff]$ ls -l
total 0
-rw------- 1 amittal csdept 0 Jan 26 12:10 first.txt
The first character will tell us if the file is a director ( with the "d" character ) or "-" if it is a normal file. Then we have "r", "w" and ""x" . For the above case the owner has permissions to read and write to the file. There are 2 ways we can manipulate the permissions. The first is with characters and the second using binary numbers.
[amittal@hills unix_stuff]$ chmod o+rwx first.txt
[amittal@hills unix_stuff]$ ls -l
total 0
-rw----rwx 1 amittal csdept 0 Jan 26 12:10 first.txt
The format is:
chmod people (+/-) rwx
Where people is "o" for others , "g" for group and "u" for the owner.
r - Read w - Write x - Execute u - The owner of the file g - The group that the file belongs to o - Anybody who is not one of the above a - All users + - Add permissions - - Remove permissions
[amittal@hills unix_stuff]$ chmod a+rwx first.txt[amittal@hills unix_stuff]$ ls -l total 0-rwxrwxrwx 1 amittal csdept 0 Jan 26 12:10 first.txt
The above changes permissions for all the 3 sets of people by giving everyone read, write and
execute permissions.
We can also use binary numbers.
: The first octet represents permissions for the owner. r w x T : The second octet represents permissions for the group. Owner: 4 2 1 7 : The third octet represents permissions for everyone else. Group: 0 0 0 0 : For each octet, start at 0 and: Other: 0 0 0 0 : +4 for read permission. : +2 for write permission. Command: chmod 700 : +1 for execute permission.
[amittal@hills unix_stuff]$ chmod 000 first.txt
[amittal@hills unix_stuff]$
[amittal@hills unix_stuff]$
[amittal@hills unix_stuff]$ ls -l
total 0
---------- 1 amittal csdept 0 Jan 26 12:10 first.txt
[amittal@hills unix_stuff]$ chmod 605 first.txt
[amittal@hills unix_stuff]$ ls -l
total 0
-rw----r-x 1 amittal csdept 0 Jan 26 12:10 first.txtThe string "605" states that 6 which in binary is "110" and that means assigns "rw" to the
owner and 5 translates to "101" so assign "rx" to everyone else.
//To Do
Cout, namespace
extern
Include files
Declarations and definitions
Multiple files