C++ Notes
Table of Contents
- 1 Requirements
- 2 Basics
- 3 Functions
- 4 Preprocessors
- 5 Classes and Objects in C++
- 5.1 Defining Classes and Objects
- 5.2 Data members
- 5.3 Function members
- 5.4 Constructors and Destructors
- 5.5 Implicit and Explicit conversion
- 5.6 Namespaces
- 5.7 Using
this
- 5.8 Operator overload: Member function
- 5.9 Operator overload: Non-member function
- 5.10 Conversion operator
- 5.11 Using new and delete
- 5.6 File IO
- 6. Data Structures
This tutorial is based on C++11. Make sure you are using the latest IDE's - XCode (Mac) or Visual Studio (Windows). You can use other IDE's but make sure it supports the latest C++ version.
IDE's that I think are good:
- Visual Studio 2019 Community - Cross-platform
- XCode - Mac
- CLion - Cross-platform
- NetBeans - Cross-platform
- Eclipse - Cross-platform
Adding files to Xcode
- Download
XCode
from the App Store. - Open Xcode application and click on
File > New > Workspace...
. - Name the
Workspace
asCPP-Notes
. - Click on the
+
sign and then click onAdd Files to "CPP-Notes"...
Creating Project
- Click on the
+
sign and then click onNew Project...
- Under
OSX > Application
selectCommand Line Tool
and click next. In that, type in the name of the product and make sure you select the language asC++
. - Save it where every you want.
- Go to https://www.visualstudio.com/en-us/visual-studio-homepage-vs.aspx and download
Visual Studio Community
version. Make sureVisual C++
package is selected and continue the installation. - Once installed, Click on
File > New > Project
or use the shortcut Ctrl + Shift + N.
- We are adding the preprocessing variable because of the way Microsoft has written its c++ compiler. For that to happen, we are creating a C++ file. Under
Solution Explorer
right click onSource Files > Add > New Item...
name it astest.cpp
and type int he following:
#include <cstdio>
using namespace std;
int main(int argc, char ** argv) {
puts("hello");
return 0;
}
From the menubar click on Build > Build Solution
4. Under Solution Explorer, right click on CPP > Properties
As shown in the image above click on <Edit...>
. In the text box type in the following:
_CRT_SECURE_NO_WARNINGS
_HAS_ITERATOR_DEBUGGING=0
Click on OK
Running the CPP files
- Open your command prompt, by doing WINDOWS + R and type in
cmd
. - To execute the built
test.cpp
, You would have to go to the project folderCPP > Build > CPP.exe
. Drag and dropCPP.exe
on the command prompt the pressEnter
, this will outputhello
.
CMake is a build system for C++. See their website - CMake - for more information.
C++ inherits most of its code style from C language, but both are very different from each other. Let's consider an example:
// This is a comment
/*
This is a block code.
*/
#include <cstdio> //<- Libraries
#include <iostream>
using namespace std; //<- scope to identifiers
int main(int argc, char ** argv) {
puts("hello"); // this statement outputs hello with a new line
printf("hello\n"); // this is similar to puts but doesn't end with new line
cout << "hello\n"; // more complex way to output without new line
return 0; // 0 means success
}
A C++ program can also be written like this (though I wouldn't recommend it):
#include <cstdio>
using namespace std;
int
main
(
int
argc,
char
**
argv) {
puts("
hello")
;
return 0;
}
Things to remember
- A statement should always end with
;
. #Include
should always be in single line without any space followed by<>
or""
.
C++ follows the following standards
A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s | t | u | v | w | x | y | z |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|
_ |
---|
- These identifiers cannot conflict with C++ 86 keywords (which includes 11 tokens)
alignas (since C++11) | explicit | signed |
alignof (since C++11) | export(1) | sizeof |
and | extern | static |
and_eq | FALSE | static_assert (since C++11) |
asm | float | static_cast |
auto(1) | for | struct |
bitand | friend | switch |
bitor | goto | template |
bool | if | this |
break | inline | thread_local (since C++11) |
case | int | throw |
catch | long | TRUE |
char | mutable | try |
char16_t (since C++11) | namespace | typedef |
char32_t (since C++11) | new | typeid |
class | noexcept (since C++11) | typename |
compl | not | union |
concept (concepts TS) | not_eq | unsigned |
const | nullptr (since C++11) | using(1) |
constexpr (since C++11) | operator | virtual |
const_cast | or | void |
continue | or_eq | volatile |
decltype (since C++11) | private | wchar_t |
default(1) | protected | while |
delete(1) | public | xor |
do | register | xor_eq |
double | reinterpret_cast | |
dynamic_cast | requires (concepts TS) | |
else | return | |
enum | short |
- Identifiers are case sensitive.
Identifiers (or variables) can be initialized by using the following syntax:
DataType VariableName = "String" or number;
You can also define a read-only variable or a constant in C++ by using the keyword const
.
see 2_3_Pointers_Reference.cpp
A variable can be called in two ways; call by value
and call by reference
.
A pointer and reference are a type of data type, which is commonly used in C/C++ programming. It is a very powerful and confusing concept. It will take some time to understand.
- A pointer can take reference of another variable or a real value
Lets understand how identifiers work. When we say int a = 10;
, an integer variable a
has a value of 10
in the memory. When we say int b = a;
, an integer variable b
has a copy of variable a
int a = 10;
int b = a; // b = 10
Pointers
So, When I say int *c = &a
, it means that pointer c
points to the reference of a
.
int a = 10;
int *b = &a;
printf("%d\n", *b); // this will print the reference value of a, which is 10
Reference
References are the address of value in the memory. The pointer points to this address while calling.
- A reference can only call a variable which is already initialized.
So, when I say int &d = b
the address if b
is stored in d
.
int b = 20;
int &d = b;
printf("%d\n", d);
See 2_4_1_Arrays.cpp and 2_4_2_String.cpp
There are two types of Arrays and String in C++, one using C style Arrays & String and the second one using Standard Library (STL), which will be covered later.
Arrays
The syntax of a C-based array is
int a[5] = {1,2,3,4,5}; // array[SizeOfArray] = {'contents'};
printf("%d\n", a[0]); // 1
Strings
A string is an array of characters terminated string or also called as null terminated string. A string is always terminated with 0.
char a[6] = {'a', 'b', 'c', 'd', 'e', 0};
printf("%s\n", a); // abcde
There are two ways to use conditional operators.
- Traditional conditional operator.
- Ternary conditional operator.
Traditional conditional operator
if..else..
are the common type of conditional statements.
int a = 10;
int b = 20;
if (a > b) {
puts("a>b");
} else {
puts("b>a");
}
Ternary conditional operator
Its a one liner conditional operator
int a = 10;
int b = 20;
printf("%d\n", a > b ? a : b); // if a is greater than b, print a else print b
It is a conditional statement, which requires an expression which should satisfy a condition. If a condition is not satisfied, then it jumps to default
. An expression should always be a constant of integer or a character. Syntax looks something like this
switch (/* expression */) {
case /* value */:
/* statement */
}
There are two types of While
loop in C++
While
loop
while (/* condition */) {
/* code */
}
do.. While..
loop
do {
/* code */
} while(/* condition */);
See 2_8_For_Loop.cpp
For loop is like while
loop but with some extra features
for (size_t i = 0; i < count; i++) {
/* code */
}
Starting from C++11, we can use range based For
loop
for (type var1 : var2) {
/* code */
}
C++ also has a way to used object oriented way of printing out contents to the terminal/command prompt. So far we have used printf
and puts
.
std::cout << "Hello World!" << std::endl;
The above code shows a bitwise stream of string to cout
. The <<
shows left shift of the content.
Creating a compiled version of cout
uses a lot of resources when compared to puts
or printf
, this is because to compile cout
the whole standard library - STL
- is copied.
A function can be defined as a block of code that is separate from the existing code; that is all the variables used in a function would only belong to that particular function. For example (pseudo code):
int a = 10;
int b = 20;
c = sum(a, b);
int sum (int a, int b){
return a + b;
}
printf("%d\n", c);
From the above the variables a
and b
in function sum()
are different from the initialized variable a
and b
.
This particular type of function is call call by value
function. Another type of function is called as the call by reference
or sometimes called as the call by address
. For example (pseudo code):
int a = 10;
int b = 20;
c = sum(&a, &b);
int sum (int *a, int *b){
return *a + *b;
}
printf("%d\n", c);
In C++, a function should be declared first before calling it. That is:
void name(/* arguments */) {
/* code */
}
int main(int argc, char const *argv[]) {
name()
return 0;
}
C++ will not compile if the function being called is written after the main function.
To overcome this problem, we have something called Forward Declaration
. For example:
void name(/* arguments */);
int main(int argc, char const *argv[]) {
name()
return 0;
}
void name(/* arguments */) {
/* code */
}
void name(/* arguments */);
is know as Forward Declaration
or prototype of name()
See 3_1_3_Function_Header.cpp and 3_1_3_Function_Header.h
The common way to do Forward Declaration
is to put the prototype in a header file. For example:
3_1_3_Function_Header.cpp
#include "3_1_3_Function_Header.h"
int main(int argc, char const *argv[]) {
name()
return 0;
}
void name(/* arguments */) {
/* code */
}
3_1_3_Function_Header.h
#ifndef 3_1_3_Function_Header_h
#define 3_1_3_Function_Header_h
void name(/* arguments */);
#endif
There are two two ways to pass values to a function
- Pass by Value
- Pass by Reference
Pass by value:
When you pass a value to a function, a copy of that value is stored in the argument.
void sum(int c, int d) {
printf("%d\n", c + d);
}
int main(int argc, char const *argv[]) {
int a = 10;
int b = 20;
sum(a,b);
return 0;
}
Pass by Reference:
See 3_2_2_Pass_by_Reference.cpp
We will talk more about pointers in the coming chapters. In C/C++ (but not limited to theses languages), when you create a variable a memory is allocated to that variable, this memory space has an address (location of it), so the reference here means we are sending the address of the variable rather than the variable itself.
For example, let us consider int a = 10;
, which means an integer variable a
has a value of 10
if you convert this in a diagrammatically you will get the following:
int a = 10;
----------
| a | 10 | --> 123456
----------
The number 123456
is the address/location of integer a
in the memory. When passing the value by reference you send this address, that means you do not create extra space for data; you just use what you have.
void sum(int *a, int *b){
printf("%d\n", *a+*b); // *a and *b pointing to the address given to them.
}
int main(int argc, char ** argv) {
int a = 10;
int b = 20;
sum(&a,&b); // address of a and b
return 0;
}
See 3_2_3_Pass_by_Reference_Const.cpp
There is one problem with pointers in C/C++, that is if you change the contents of the address in sum()
function you will change the value of the variable. For example If we add a new integer a=30
or *a=30
variable to sum()
void sum(int &a, int &b){
a = 30;
printf("%d\n", a+b);
}
// or
void sum(int *a, int *b){
*a = 30;
printf("%d\n", *a+*b);
}
The value of a
is completely changed, for this not to happen we will have to use a keyword called const
.
void sum(const int &a, const int &b){
a = 30;
printf("%d\n", a+b);
}
// or
void sum(const int *a, const int *b){
*a = 30;
printf("%d\n", *a+*b);
}
Automatic variable
By default, C++ uses automatic variables in every function. Whenever the function is called the variable local to it is initialized on a stack. For example
void name() {
int a = 10;
printf("%d\n", a);
a = 30;
}
int main(int argc, char const *argv[]) {
name();
name();// this will always print the same thing
return 0;
}
Static variable
Unlike automatic variables Static variables do not get created on every function call, they just use whatever was previously defined. Don't forget to use const
if you don't want to change the value.
To return a function, we would have to type in the return type and use the keyword return
at the end of the function. For example:
int number(){
return 10;
}
int main(int argc, char const *argv[]) {
printf("%d\n", number());
return 0;
}
You can call a function by pointing to it, the same way you point to a variable. The only difference is that the data type of the function should match with the data type of the function pointer. For example
void name( {
puts("hello");
}
int main(int argc, char const *argv[]) {
void (*function_pointer)() = name;
function_pointer();
return 0;
}
See 3_6_Overloading_Fucntion_Names.cpp
In C++ you can have multiple functions with the same name, but the signature (data type) should be same all over the function.
See 3_7_Oveloading_Operators.cpp
In C++ you can change the definition of the following 38 operators:
+ | - | * | / | % | ^ | & | | | ~ | ! | = | < | > | += | -= |
*= | /= | %= | ^= | &= | |= | << | >> | >>= | <<= | == | != | <= | >= | && |
|| | ++ | -- | , | ->* | -> | ( ) | [ ] |
That means an addition operator can be turned into multiplication operator.
See 3_8_Variable_Arguments.cpp
In C++ you can have multiple arguments given to a function, this can be achieved by adding ...
in the function arguments space.
There are four macros that needs to be called when using a variable arguments:
- va_list:
va_list fa
is used as a parameter. - va_start:
va_start(ap, parameter)
initialize a variable argument list. - va_arg:
va_arg(ap, type)
gets the next available argument of a data type. - va_end:
va_end(ap)
Ends using variable argument list
See 3_9_Recursive_Function.cpp
In C++ you can call a function itself. For example:
int main(int argc, char const *argv[]) {
main();
return 0;
}
These types of functions are called recursive functions. These functions as an alternate to For loops.
The preprocessors are used to process the code before sending it to the compiler. The most common way is the file inclusion using #include <>
. You can also use macro preprocessors by using #define NUMBER 1
, these acts like a string substitution.
When you open a .h
the contents of the file you often see looks something like this:
#ifndef main_h
#define main_h
void function_name();
#endif /* main_h */
They are called as an "include guard" which checks for inclusion.
Another type of preprocessor is used by using #pragma
that are used (or targeted) for specific compilers and architectures.
You can define a macro constant by using #define macro
. For example:
#define NUMBER 1
int main(int argc, char const *argv[]) {
printf("%d\n", NUMBER);
return 0;
}
When the above code is compiled the NUMBER is replaced by a literal value before the code reaches to the compiler. At this point you cannot get its address or use pointers.
See 4_2_Include_File
To include a file in a C++ file you would have to use #include "file_name.h"
. This will place all the contents in the cpp
before the code is sent to the compiler.
See 4_3_Preprocessor_Conditions
Preprocessor consists of different types of conditional compilation
#if | Opening `if` condition |
#else | `else` condition |
#elif | `else if` condition |
#ifdef | `if defined` condition |
#ifndef | `if not defined` condition |
#endif | `end if` condition |
Also, there are two alternatives for #ifdef
and #ifndef
, they are:
#if defined(macro)
#if !defined(macro)
Macro's can also take parameters and replace them when called. For example:
#define ADD(a,b) (a+b)
int main(int argc, char const *argv[]) {
printf("%d\n", ADD(10,20));
return 0;
}
You should always be careful when using parameterised macros. See 4_5_Macro_Problems.cpp for more details.
If you want to use complex macros you can use line continuation
by add \
at the end of the each line. For example:
#define LOOPER(i) do \
{ \
printf("%d\n",i++); \
} while (i < 3);
There might be a situation where you might have to define a header file in another header file and when called in a C++ file you might include both header files. When you compile this you will have a Build fail
, to over come this we have to include something called as Include guard
. It looks something like this
#ifndef _HEADERNAME_H
#define _HEADERNAME_H
...
#endif
C++ is a an Object Oriented Program, that's what makes it different from C programming language. A class is define by using class
keyword followed by class name. For example:
class name_t {
int i; // Data members
public: // Function members
name_t (arguments); // Constructor
~name_t (); // Destructor
};
Few points to remember:
- A class can have multiple constructor and only one destructor.
- A class when called and naming it is called an instance of that class. Example
name_t name;
,name
is an instance of classname_t
. - Using classes you can allocate memory properly.
More information can be found here.
There are different ways to define a class. For example
class name_t {
int i;
public:
void some_name (arguments){ /* do something */};
};
int main(int argc, char const *argv[]) {
name_t obj1;
return 0;
}
Another way is to use private
keyword, you can then use this to define private
variables and function after public
. For example:
class name_t {
public:
void some_name (arguments){/* do something */};
private:
int i;
};
int main(int argc, char const *argv[]) {
name_t obj1;
return 0;
}
The public functions can be used outside, just declare it in the class file and define it outside the class
. For example:
class name_t {
public:
void some_name (arguments);
private:
int i;
};
void name_t::some_name (arguments){/* do something */};
int main(int argc, char const *argv[]) {
name_t obj1;
return 0;
}
The code can be divided into 3 stages:
-
Interface: Usually kept in the header file.
class name_t { private: /* data */ public: void some_name (arguments); };
-
Implementation: Usually kept in an implementation file
void name_t::some_name (arguments){/* do something */};
-
Usage: Usually kept in
cpp
fileint main(int argc, char const *argv[]) { name_t obj1; return 0; }
In C and C++ we can find keyword called struct
, when used in C++ it is an object. The different between a struct
and class
is that, struct
by default has public
data members, where as class
has private
data members, everything else is the same. For example:
struct name_t {
/* data */
};
is same as
struct name_t {
public:
/* data */
};
You can define a same function with different signatures in C++.
See 5_4_Constructors_Dstructors.cpp
A constructor can be used to send in arguments while initializing a class. Destructors are used to clear the memory after the program ends, in C++ destructor are always called at the end of the program by default.
By Default C++ does implicit conversion. To make an explicit conversion we need to use explicit
keyword for a constructor.
For example:
class name_t {
public:
explicit name_t (arguments);
virtual ~name_t ();
};
Namespace in C++ acts like a scope to a group of classes, functions etc... A Namespace can be created by using namespace
keyword. For example:
namespace name {
};
An object in C++ can access its own pointer, to do so, this
keyword is used. You can print out the address of a pointer by using
printf("%p\n", this);
See 5_8_Operator_Overload_Member_Function.cpp
Any function that belongs to a class is called a member function. Operator overload can be a part of member function.
See 5_9_Operator_Overload_Non_Member_Function.cpp
Any function that does not belong to a class is called a non-member function.
See 5_10_Conversion_Operator.cpp
You can use +=
to concatenate a string with a rational number that belongs to a member function.
C++ allows you to allocate and delete memory for different data types using two keywords, new
- To allocate memory and delete
- To deallocate memory. For example:
class name_t {
private:
/* data */
public:
name_t (arguments);
virtual ~name_t ();
};
int main(int argc, char const *argv[]) {
name_t *a = new name_t(); // to allocate memory
delete a; // to deallocate memory
return 0;
}
In this section we will look at how to read and write files using fstream
.;
Reading a file in C++ can be done using ifstream
, this data type has many functions associated to it but we want open
, good
and close
. open
opens a file from the memory, good
checks if the state of stream is good or not and close
closes the file after reading from it.
Writing file can be done using ofstring
, like ifstring
, this data type provides the same functions - open
and close
. If a file already exists with that name, its over written, this can be changed using ios::app
option that appends any string given to it.
A data structure is a group of data elements grouped together under one name. A structure can have multiple data types grouped together to form a way of representation.
It has a group of data types that can be called by name. It can be represented as:
struct STRUCT_NAME {
DATA_TYPE NAME;
};
You can access them as:
STRUCT_NAME some_name;
some_name.NAME