You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Main function is a special function it return 0 if you dont return anything
TIP: Think of operators as function
Each file of cpp is converted to and .obj file and linker bundels all these files into a excutable binary (linker job is to resolve symbols)
Statments that starts with # are preporcessor statments
Different types of preporcessor are, "include, define if"
Chars are useful to do pointer arithmatics since they represent 1 byte of memory
LINKER
linker job is to link different obj file into one exe file
VARIABLES
Used to store data
they occupy memory
The only real difference between the datatypes is how much memory they occupy
char and bool are 1 byte, short is 2 byte, long is 4 byte, long long 8 byte
way to store decimal is using double(8 byte) or float(4 byte)
NOTE: bool require 1 byte and not 1 bit, since we cant access a bit in the program
so we have to use 1 byte. We can do some tricks to store 8 bools in 1 byte
break, continue, and return are control flow statments
If a continue is encountered in a loop, it goes into the next iteration
without going in to the remaining part of the loop
If break is encountered the code exits the loop
If return is encountered it exits the function
POINTERS
Pointers are a data type that is used to store memory address of a memory
block
In laymans term pointers are a variable of int datatype
You can create a pointer using int `void* ptr = nullptr
Types in pointer are just syntax sugar and it helps us find what datatype is
stored at a given memroy address
Example of int pointer
Here ptr stores the address where a is stored in the computers memory
int a = 10;
int* ptr = &a;
To get the values stored at a memory location we use * operator
Example:
int a = 10;
int* ptr = &a;
*a = 11;
cout << a; // 11
Example of how to create and delete memory from heap
char* buffer = newchar[4] // create a 8 byte memroy and return a point the the start of itmemset(buffer, 0, 8)
delete[] buffer
REFERENCES
References are just pointers in disguise, References are syntax sugar
Reference only exits in source code, references dont occupy memrory like a real variable
Syntax
int a = 10;
int& ref = a;
ref = 2;
print(a) // 2
Here are 2 examples of code one using reference and one using pointer
// Pointerintmain() {
int a = 5;
increment(&a);
cout << a; // 6
}
voidincrement(int* ptr) {
(*ptr)++;
}
// reference intmain() {
int a = 5;
increment(a);
cout << a; // 6
}
voidincrement(int& ref) {
ref++;
}
Intresting facts about ref
You cannot change what a reference refers too
which means when declaring a reference it has to be assigned to something
OOPS
CLASS
Classes allow us to group variables together into a type and also add functionality to it
Diffrence between struct and class
Classes are private by default while struct are public by default
Struct are there to add backward compatiblity with c
STATIC-KEYWORD
Static keyword is used to mark a variable as static in cpp
Static when used inside a class makes all the instance of the class use the same variable
While if static is used outside of the class it make the scope of the variable only to the translation unit its defined in
if you want a variable to be used across all the instances of a class you can make it static
Static method cannot access non-static variables for obvious reasons
Static is used of data that we dont want to change between class instances
Syntax static int x = 10;
LOCAL STATIC
You can make a variable static to a local scope as well, this makes the variable
declared just once, and use the same instance of the variable to be used across
subsequest call to the scope/function.
ENUM
Syntax
enum Example : [type] {
A = 0, B = 1, C = 2
}
Note: You can assign a type to the enum to reduce the amount of memory a enum takes
By default enum type is int taking 32 bits of memory
At the end of the day enums are just integers behind the scenes
CONSTRUCTOR-BASICS
Defination: constructor is a special function that run when a instance of a class is initialized
Syntax:
ClassName(parameters) {}
Each class in cpp has a constructor called default constructor
If you want to get rid of the default constructor either make it private or
ClassName() = delete;
Note: In language like java and c-sharp all the primitive types are initialized by
default, while thats not the case in c++
In short basic use of constructor is for initilizing the instance and its memory
DESTRUCTOR-BASICS
Destructor is called when a instance of a class cleared away
Syntax
~ClassName() {}
Primary use of destructor is to delete the memory allocated for the instance of the class
Real world examples are: if you have heap allocated objects, you can delete it in the destructor
You can also call a destructor manually.
INHERITANCE
A class can have heirarcy, it can inherinit variables and methods from another class
Inheritance is useful in getting the functionality of a class that it inherits, preventing code duplication
Syntax
classEntity {}
classPlayer : publicEntity {}
The above example Player class inherits the Entity class
If there is a function that takes entity, we can pass player class instance and the code will work.
This is because Player class will always have everything entity has, this pheonamena is called polymorphism
VIRTUAL FUNCTIONS
Virtual function allow us to override methods in base class
Note: Virtual functions are not free, there is a performance penelty associated with virtual functions
firstly there is need for extra memory, to store the v_table and a pointer to the v table is stored in the base class,
and every time we call a virtual function we have to go through the v_table to determine which function to call, so there is a
extra jump in the execution, this only becomes a issues when we are working on some embedded system or similar, other than that
its not an a issue
Virtual function use something called diyamic dispatch which is implemented using v_table
PURE VIRTUAL FUNCTION
This is similar to abstract method or interface in java or c-sharp
Syntax for pure virtual function is vitual int function_name() = 0;
You cant create an instance of a class that has a pure virtual function
A pure virtual function is a function that dosent have a body, body of this function is defined in a the class inheriting it
Adding a pure virtual function to a class will also make it abstract
VISIBLITY
visiblity is used to restrict the access of members and methods of a class by other code
there are 3 types of visiblity in c++, private, protected, and public
Private: only the code inside the class can access the variable, there is a exception for firend
Protected: Only the class itself and the class inheriting the class can access the variables and methods
Public: Anyone can access it
ARRAYS
Array is a group of variables stored in a single row in a memoroy
Declaration int array[5] this is a declaration on stack
you can access a values from array using array[0], if you try to access value of bounds
you will get a MemoryViolation error in debug mode, but you might not get that error in
production
array is just a pointer
array[2] can be rewirtten as with *(array + 2), this is possible because
arrays are stored in contiguious block memory
we can also create arrays on heap int* another = new int[5];
Array created on heap will be there untill the program ends or we manually delete it
from the code delete[] another, diffrence between heap and stack allocated arrays are lifetimes
We have to maintain size of a heap allocated memory in cpp, also there is a jumping around with heap allocated memory
so its has a performance overhead
To overcome this issue we have array datastructure introduced in c++11 library, which does built in bound checking
and also has a size variable, to keep track the size of array, and it also has a performance overhead becuase of all the
additional features
STRINGS
A string in a array of characters in cpp
To define a string you can do const char* name = "Joan" or std::string name = "John"
A const char* ends to a null character \0 to denote end of the string
Standard c++ library for string has many operation for string
TIP: dont pass class directly to a function this creates a copy of the string instead pass a reference
More points comming soon....
CONST
const is a keyword in cpp, that is used to add restriction to the code on how it
can modify certain variable or data
adding a const dosent change how a code will run actually, but instead it enforces some rules
on the developer writing the code
Syntax const int a = 2
addint const to a varible in this way prevent it from being modified in other part of the code
making it readonly
const with pointer works in 2 ways either change the pointer it points to or the contents of the the pointers
To prevent modification of the content of the pointer use const int* a = new int, to prevent modification
of the location where pointer points to use int* const a = new int, and to prevent both the cases use const
on both the places(tip: here the position of const is relative to * symbol)
const with methods: if const is added to a method in a class like int get_x() const {}(on the right side of the method name) this means
that the method is not going to modify the class it will just read the contents of the class
This looks a bit incomplete
MUTABLE
This is very badly structured
Mutable with const
If mutable keyword is added before a variable, this gived the const class methods to modify
this variable
Mutable with lambda
Adding mutable keyword to a lambda function lets the lambda function modify that variable
// NOTE: Here = means that you are passing the value// You can replace = with & to pass the argumentes as refrence insteadint x = 0;
auto f = [=]() mutable {
x++; // this would not have been possible without mutable keyword
}
// value of x here is still 8, mutable keyword just created a copy internally to make your code cleaner,auto f [=]() {
int a = x;
return a++;
// Note here wer are not modifying x but instead creating a and chainging that
}
MEMBER INTIALIZER LIST
Intializer list is an alternative to intializing variables using constructor
In the above example m_Name will be initialized to Default, or any values supplied by user
The benefit of the member initializer list is not just limited to, better syntax and clean constructor,
but also have performace advantage if a member variable is not listed in the initializer list, then its
initialized 2 times, once were its declared, and once in the constructor, if
its declared in the member initalizer list then its only intialized once
NOTE: It dosent matter the order in which you list the members in the constructor,
they will be initailized in the order in which they are declared in the class,
if not followed this can lead to dependency problems, so make sure to write the list in
the same order as declared
Example of incorrect order of initializer list, here m_score should come first since
its declared first
Terynary operator is a way to write if else condition in a different way
WAYS OBJECTS ARE STORED IN CPP
there are 2 types of memory to alocate memory to a object, stack and on heap
to alocate memory on heap use new Entity* e = new Entity() keyword and to delete the object from the
use delete keyword delete e
Always try to allocate memory on stack since its faster and self managed, only allocate
objects on heap when your object is too large for stack memory or you want to manage the lifetime of your object
UNDERSTANDING NEW KEYWORD
In cpp new keyword is used to allocate memory on heap
New keyword is a operator which calls a function(malloc) and passes the size argument
and returns a pointer to the block on memory allocated, new keyword also calls the constructor
If new keyword is used we need to use delete keyword to free the memory, that was allocated by new
IMPLICIT AND EXPLICIT CONVERSION
To be added soon...
OPERATOR OVERLOADING
Operator Overloading is used to use a operator like + or - to be used as function
For example if there is a code like 3.add(4) we can overload + operator with add function and
write this code as 3 + 4
Syntax:
intoperator+(int a) {
returnclass.x + a
}
Note: Operator overloading is just like writing a function, and just because
you can overload a operator dosent mean you should do it, don't do it unless it makes perfect sense and the operator
is very intutive to use, because this can make your code harder to understand and reason about
THIS KEYWORD
this is a keyword in cpp that is used to reference the instance of the class inside the class method
or member function
the keyword becomes useful for example if you want to call a function outside of the class and that function
takes the reference of the class as a argument then you can pass *this and call the function
TODO: See if you can bypass the visiblity using the above method
Below is another example, of this being used in constructor
classEntity {
int x, y;
public
Entity(int x, int y) {
// note here we cant do x = x since it will reassing x to itselfthis->x = x;
this->y = y;
}
}
Type of this in a member function is Entity *e and in a const function is const Entity *e
OBJECTS LIFETIME
A scope can be anything a function a if block etc..
Stack and Heap are 2 type of memory allocation strategy used in cpp
A stack based variable is destroyed when the variable goes out of scope
A very good example levaraging stack based lifetimes, are unique pointers, scoped locks etc
SMART POINTERS
There are 3 type of smart pointer, namely unique pointers, shared pointers, weak pointers
Unique pointers: Unique pointers are pointers that clears the allocated memory
automatically when the pointer goes out of scope
Syntax
# include<memory>intmain() {
// syntax 1
std::unique_ptr<Entity> entity = std::make_unique<Entity>();
// or// syntax 2
std::uniqye_ptr<Entity> entity(newEntity());
// TODO: Explain how// syntax 1 is better than 2 since it provides exception safety
}
One cannot copy a unique pointer
Unique pointers have almost no overhead, unqiue pointers cannot be shared
between variables, since sharing a unqiue pointer would mean that if one
goes out of scope and is deleted other pointer pointes to a memory thats not
valid
Shared pointers: Shared pointers are scoped pointers that can be copied they
work by maintaining a ref count, each time a pointer is copied, the ref
count increases by 1, and when a shared pointer goes out of scope the ref
count is decreased by 1. The memory is freed automatically when the ref
count reaches zero.
Shared pointer has a small over, since they maintain ref count
Weak pointers: Weak pointers are similar to shared pointer, but when a
shared pointer is gets shared with a weak pointer the ref count dosent
increase, which means that even if there is a weak pointer to a memory
address in scope, the memroy will still be freed since shared pointer ref
count is zero
One can ask a weak pointer if its still valid or not
TODO: Above 2 points are not very clear please re-write the same
COPY AND COPY CONSTRUCTOR
When you use = operator to assign a object to a variable, it leads to a copy
of the actual object, compiler creates a new object of the class copies the
private members of the from the object to the new object
Vectors are dynamic arrays which means, that you dont need to specify the size
while initializing, you can add elements as needed, and the array grown dynamically
NOTE: The Elements in the array are stored as a single contingous block of memory
Remember: ALways try to pass the vector as reference to a function, else it will
lead to a complete copy of the vector, this is even true while using vectors in a
range based for loops, make sure to use ref there as well
Optimizing vector class of standard library, code in optimized vector
The way vector works, is that you start with a array, and when you add elements to this array
and if the size of the underlying array is not enough the another arrray is created
and the existing elements are copied to the new array
Data inside the vectors are stored in heap, and only the metadata of the vector like
size etc is stored in stack
USING EXTERNAL LIBRARIES
There are 2 ways to link external libraries in cpp using, Dynamic linking and static linking
In static linking the library is put into the soruce code
Static linking is techinically faster because compiler can perform optimizations
Dyanmic linking, means that the library is actually linked at the runtime
Lets talk about static linking.
We need to get the binaries either in compiled form or in source code and complie it by ourself
There are 2 major parts to the libary, one is the includes folder that contains the header files i.e function
declaration etc, and other is the lib folder that contains the actual implementation
Header files tells us what functions are available in the libaray
So we need to configure the compiler to point to the header file, and configure the
linker to point to the library file
TODO: Dynamic linking is a bit confusing to understand please restructure
Dynamic linking also works very similar to the static linking, we need to add
header file, but instead of specifying the lib file, we add a file that has location
of the functions in the actual dll file, this methods need the dll file to be needed
before we are able to run the program
There is also another way were, we dont need the dll file and the program can run
without the dll file, and when it calls a function that is defined in the dll file
it then needs to search the dll file for the specific function being called
HOW TO RETURN MULTIPLE VALUES
You can return a structure from a function
One way is add the return variables as argument to the function
Another way is to return a array, but here each return value needs to be of the same type
One more way is to create a tuple and return
Here returning a struct is the most readble code, and taking output arguments as input is the most
efficient way
In output arguments as input, you pass the variables refs to the function and the function modifies those variables
so you can read back the updated values
TEMPLATES
If compared to other languages templates can be compared to generics in c# and cpp
but are infinitely more powerfull then generics
Example of template: if there is a function that can take multiple type of parameters
then we can define a template and use the template type instead.
template <typename T> and then we can use it in a function like this void hello(T value)
The way it works is that when we define a function with a template, and the function dosent actually gets defined
unless we call it, so based on when we call it and how we call it, based on type the function gets created,
and complied
So in summary templates gives us the ability to run specific code based on what type we pass
Note: The template dosent actually exists untill we call it, so if you have a syntax error in the template
you wont notice it unless you call it.
HEAP vs STACK
STACK allocations are a lot faster than help allocation
Heap allocation calls new which in turn calls malloc which asks the operating system for the memory
and does all the book keeping going throught the freelist to find a free memory block
and marking the memory as used etc, and returns the pointer to the memory
Here the performance difference is the allocation
Always try to allocate on stack unless you cant maybe due to size of the data or lifetime
In the above exmple if PR_DEBUG is set to 1 then LOG(x) will print x to the terminal
else if PR_RELEASE is defined then LOG(x) will be replaced with nothing, this way
we can use macros, macros are used a lot for debug purpose, or enable or disable some stuff
We can escape a character in macro with a \, this way we can write macro in multiple line by escaping a new-line character
AUTO keyword
Auto keyword is used to automatically deduce type of a variable at compile time
auto x = "hello"; Here the type of x will be const char*
we should use auto judiciosly since in can make your code hard to understand and reason
Standard array std::array
Std array in cpp is just a simple c style array with a size attached to it
Syntax
std::array<int, 5> a;
a[1] = 1
Here int is the datatype we need to store, and 5 is the size
We should use, std array instead of c style array since it has no overhead,
it performs bound checking in debug mode, and can be used with iterators, also
the size variable is not stored since its a template argument, so no extra memory is required
Also the variables are stored on stack as opposed to heap in vectors
INTRO to functional pointers
Functional pointers are a technique in which variables can be assigned to functions
and the variables can be used to call the actual functions
functional pointers are pointers to the actual location of the function in the binary
The [] brakets specifies the capture group, if you want to pass
external variables to the lambda fuction you can specify the capture speicifies
how to pass the external variables, either by reference [&] or by values [=]
The () specified the arguments that will be passed to the function
{} specified the body of the function
NOTE: You can also specify a specifier between arguments list and body, if we want
to modify the vairables that are captured by values
TODO: Please elobrate 6th point
NAMESPACES
Namespace in cpp is used to avoid naming conflicts between symbols for example
if we have 2 print function with the same signature then, we would not know which
function to call, to solve this issue we can wrap the function with a namespace
and call the function thats required based on the namespace specified
virtual destructors are similar to destructor but, Let me explain with a example, If there is class base
and a class derived which inherits base if you create a new object of type derived everything works as
expected, but when you create a class of type base and create it using the derived class, the vairable or memory
when freed dosent call the destructor of the base class, inorder to make it work, we need to mark the destructor of the
base class as virtual.
In short if we want to make your class safe for use that will be inherited, its a good idea to
make the destructor function as a virtual function, we have a example for it at virtual_destructor.cpp
CASTING
Casting in cpp is can be of 2 types, cpp style casting and c style casting
Casting means converting a type in cpp to another type
Types of casting in cpp
Static cast <static_case>(int)
Convert a type statically
Reinterpret cast <reinterpret_cast>
Used for type punning, i want to take a pointer a reinterpret as a different value
Dynamic cast <dynamic_cast>
This cast is used to convert a value dynamically, this can give null if the type conversion is successfull, this does a runtime check
Const cast <const_cast>
Removing a const or adding a const
Dynamic cast: This is a cast introduced in cpp, it works by adding a check during the runtime, that if we can convert a
given type to another, it works by storing the type information in a table called RTTI(Run time type information)
If the type cannot be converted it returns a null, this adds some overhead to the code execution
We can also use it to determine if a given variable is of a given type, similar to type() in js or isinstance() in cpp
PRE-COMPILED HEADERS
Pre-compiled headers are headers that have been already compiled, and the program uses the precompiled headers
This helps speed up the compile times a lot, since header files are very large, and each translation unit including it
this makes, the compilation a lot slower
Instead we can include all the commanly used headers into a header file and then compile that header and use that instead
STRUCTURED BINDING
Structured bindings are a way to help you return multiple values from a function
Instead of finding ways around, we can return a tuple from the function and use structured binding to
bind a variable(with a name) to each of the return value
Strings are a great place to optimize, strings are bad because each string operation
allocates memory, which is slow, to avoid that we can use string_view instead since it
gives us a view into the orignal string without having to make copy of that each time
string_view works like this, it gives us a window into the existing string, you give it
a pointer and the lenght, and in this way we can read the string without copying the data
std::string Name = "Hello";
std::string_view firstLetter(Name, 1) // firstLetter = H
Small string optimization
In cpp std library, if a string is less than a given length, it is not allocated on heap
instead cpp optimizes it, to be allocated on stack, the lenght of the small string can
change based on the std library used, but generally its 15, so any string thats less then 15 char
it allocated in a buffer, thats a small optimization added to the cpp std lib
NOTE: This only works in release mode
SINGELTONS
Singeltons are useful when we need just one instance of a class, like a renderer
We can create a singelton class in cpp, by some hacks, like making the constructor private,
delete the copy constructor, creating a static instance variable and a static get method, to access it
The above code in cpp can lead to undefined and unpredictable behaviour, because in
cpp there is no rule on the order of how the function arguments are evaluted
the function can have 0, 1 or 1, 0 or 0, 0 in the argument list
The compiler can evaluate these expressions in parallel
MOVE SEMATICS
Moving is a way to move the data from one variable to another without coping the data from one variable to another
This can be said as a optimization step, since copying data is a expensive task
We can create a construct with a rvalue reference to move the object, r values are temprory in nature, so it
makes sense to move the value, you will find a example of move semantics in move_semantics.cpp
You can make a l-value move by casting it into a r-value ref or using std::move, this will call the move constructor
or the move function that for the overload