If uniform initialization is used with no value. Default is 0.
1
int numRings{}
Integers
Fixed width integers
Will give fixed sized integer in all architecture.
1 2 3 4 5 6 7 8 9 10 11 12
#include<cstdint>// for fixed width integers
/* * 8 bit singed integer. Many systems consider them as chars. So it is better * to not use them if using integers is the purpose */ int8_t var;
uint8_t var; // 8 bit unsigned integer
intN_t var; // N = 16, 32, 64 bits signed integer uintN_t var; // N = 16, 32, 64 bits unsigned integer
Fixed width integers performance is machine dependent. To get the fastest integer type on a specific machine use int_fastN_t. It will give the fastest integer which is at least N bits long. N = 8, 16, 32, 64.
1
int_fast32_t var;
To get the smallest integer which is at least N bits long use int_leastN_t where N = 8, 16, 32, 64.
1
int_least64_t var;
Floating point numbers
Setting precision of a floating point number:
1 2 3 4 5 6 7 8 9 10 11 12
#include<iostream> #include<iomanip>// for std::setprecision()
intinteger(50); // integer floatf(0.05f); // used f suffix as default literal is double type doubled(0.31416); // double floating literal inthex(0xa0); // hexadecimal literal intoct(012); // Octal literal intbin(0b1010); // Binary literal
intlongNumber(1'23'570)// c++14 only
Constants
1 2 3 4 5 6 7 8 9
constdoubleavg(6.023e23); constdouble massEarth; // This is not allowed
intx(50); constinty(x)// This is allowed constexprdoubleadg(9.8); // Compile time constant. Value of the constant // must be resolved in compile time otherwise // will generate an error. This is c++11 feature
Variable Scopes and duration
shadowing:
1 2 3 4 5 6 7 8 9 10 11 12
intnumber(5);
if(number == 5){ int number; // local variable
number = 10; // will effect only local variable // this is shadowing
std::cout << number << std::endl; // will print local }
std::cout << number << std::endl; // will print outer block number
Global scope operator
:: is the global scope operator.
1 2 3 4 5 6 7 8
intx(50); // global variable
intmain(){ intx(40);
std::cout << x << std::endl; // will print local x std::cout << ::x << std::endl; // will print global x }
Internal Variable: Can be used anywhere in the file they are defined but not out of the file.
External Variable: Cab be used across multiple files. Global variables are by default external. static keyword can be used to make them internal.
Console Input/Output
Console I/O Methods
std::cin is used for console input. std::cin takes inputs untill first whitespace.
std::getline() is used to take whole line as input.
1 2 3 4 5 6 7 8 9 10 11 12
#include<iostream> #include<string>
intmain(){ std::string name;
std::cout << "Enter Name: "; std::getline(std::cin, name); std::cout << "Your name is: " << name;
return0; }
Error Handling in Console Input
std::cin takes upto newline from the input stream. So it will be a problem if any other input function is used after taking numeric input from std::cin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#include<iostream> #include<string>
intmain(){ std::string name; int select;
std::cout << "Select: "; std::cin >> select;
std::cout << "Enter name: "; std::getline(std::cin, name); // will not work std::cout << "Your name is: " << name << "! You have selected: " << select;
return0; }
To solve this problem std::cin.ignore(n, ch) can be used where n is the number of character to ignore from the input stream before ch character is found.
/* * Input will fail if a valid number isn't typed. * if input fails, cin will set fail flag and stop extracting * characters from input stream. */ if(std::cin.fail()){ std::cout << "Please enter a floating number" << std::endl; std::cin.clear(); /* Clear fail flag */ std::cin.ignore(32767, '\n'); /* Clear input stream */ } else{ return d; } } }
std::cout << "There are " << inStore[Store::LM7805] \ << " LM7805 in store" << std::endl;
return 0; }
Relation Between Array and Pointer
Arrays are actually pointers. It points to the first element of the array.
1 2 3 4 5 6 7 8 9 10 11
#include<iostream>
intmain(){ int arr[2] = {1, 2};
/* Following two address will be same*/ std::cout << arr << std::endl; std::cout << &arr[0] << std::endl;
return0; }
Difference Between Array and Pointer to Array
The type of the array is int (*)[n] if it is an integer array but the type of the pointer to that array is int *. Array type contains the size information of the array. When array is dereferenced or assigned to a pointer it implicitly converts itself into type * from type (*)[n] so size information is lost. Here is an example of this behaviour:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#include<iostream>
intmain(){ int arr[5] = {1, 2, 3, 4, 5}; int *arrPtr = arr; // arr is converted from int (*)[2] to int *
/* Will print the size of the array which is 5 * 8 bytes */ std::cout << sizeof(arr) << std::endl;
/* Will print the size of the pointer which is 8 bytes */ std::cout << sizeof(arrPtr) << std::endl;
return0; }
String Constants Using Fixed Sized Array and Pointer
intmain(){ /* * keep the string constant in memory with r/w access * and return the pointer to it. * So string constant can be changed any time later */ char arr[] = "hello, world";
/* * Keep the string constant in read-only section of memory * so it can't be changed */ char *text = "GNU is not Unix";
/* As it is a constant so its better to initialize following way */ constchar *text = "GNU is not Unix";
arr[0] = 'g'; // This OK
/* * In this case as string constant is kept in read-only * memory, doing this will generate segmentation * fault */ *(text + 2) = 'O';
std::cout << arr << std::endl; std::cout << text << std::endl;
return0; }
Pointers
Definig NULL Pointer C++ Way
From C++11 nullptr keyword can be used to define a NULL pointer.
char ch = 'P'; // pointing to a char ptr = &ch; std::cout << static_cast<char*>(ptr) << std::endl;
return0; }
Converting A Pointer Address to Integer
Using reinterpret_cast<>:
1 2 3 4 5 6 7 8 9 10
#include<iostream>
intmain(){ int x = 17; unsignedint addressX = reinterpret_cast<int>(&x);
std::cout << addressX << std::endl;
return0; }
Dynamic Memory Allocation
There are three basic type of memory allocation:
Static memory allocation: Static and global variables. Allocated when program runs. Persist throught the program life. Memory is taken from the stack.
Atomatic memory allocation: Function parameter and local variables. Allocated when enter into relevent block and deallocated when exited the block. Memory is taken from the stack.
Dynamic memory allocation: Memory allocated and deallocated dynamically. Memory is taken from the heap.
Allocating Memory dynamically
Dynamically memory is allocated using the new keyword.
1 2 3 4 5 6 7 8 9
#include<iostream>
intmain(){ int *ptr = newint; // dynamically an integer is allocated.
*ptr = 5;
return0; }
Deallocating memory
Memory is deallocated using the delete keyword.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#include<iostream>
intmain(){ int *ptr = newint(5); // memory is allocated to the pointer
/* memory is deallocated. * memory is realesed by os. */
delete ptr;
/* still the pointer is holding the memory address * so its better to make it null */ ptr = 0; ptr = nullptr; // c++11
return0; }
Memory leak
memory leak happens when allocated memory can’t be deallocated.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#include<iostream>
voiddoSomthing(){
/* Memory is allocated but not deallocated * so memory leak happens when the variable * goes out of scope as there is no way * to deallocate in that case */
int *ptr = newint; }
intmain(){ doSomthing(); return0; }
1 2 3 4 5 6 7 8 9 10 11 12
#include<iostream>
intmain(){ int *ptr = intnew;
int val;
// assigning to a address with out deallocating ptr = &val; // memory leak
return0; }
1 2 3 4 5 6 7 8 9 10
#include<iostream>
intmain(){ int *ptr = newint(5);
// allocating new memory without deallocating previous one int *ptr = newint(10);
return0; }
Reference Variables
Create alias for a variable. Basically share same memory address. Must be initialized with an addressable object. Can be used in function to pass parameter by reference.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#include<iostream>
intmain(){ intx(10); int &y = x; // reference variable
/* * will output: * 10 * 10 */ std::cout << x << std::endl std::cout << y << std::endl
return0; }
For Each Loop
Only works from c++11 above
Can’t be used in case of decayed arrays and dynamically allocated arrays.
/* lambda function example: used to make ~bitvect */ std::transform(bitvect.begin(), bitvect.end(), bitvect.begin(), [](bool b){ return b == 1 ? 0 : 1; }); return0; }
Syntaxt:
[&](){}: Capture all outside variable by reference. [=](){}: Capture all outside variable by value. [&x](){}: Capture only outside variable x by reference. [x](){}: Capture only outside variable x by value. [&, x](){}: Capture all outside variable by reference but x by value. []() -> Type {}: To specify return type.
/* Have to initialize first otherwise linker error will generate */ int ID::m_id = 1;
intmain(){ std::cout << ID::getID << std::endl;
return0; }
Member Types
In C++ classes can have memeber types or nested types. They make the class easy to maintain. For example in the following example it will be easy to change the type from int to double. It need to change in only one line.
C++ structs and classes are same. Only difference is that all members in structs are public.
Friend function and friend class
Friend functions and classes can access the private members of a class. In the following example printWeather() is friend of both Humidity and Temperature class. So it can access private members of both classes.
// This is not a member function only a friend function // This could defined outside of the function too friend Point operator+(cost Point &p, constint n){ returnPoint(p.m_x + n, p.m_y + n); }
friend Point operator+(constint n, const Point &p){ return p + n; }
friend Point operator+(const Point &p1, const Point &p2){ returnPoint(p1.m_x + p2.m_x, p1.m_y + p2.m_y); } };
intmain(int argc, char *argv[]){ Point p1(10, 20); Point p2(4, 5); Point p3 = p1 + p2 + 5;
p3.print();
return0; }
Using Normal Function
If there is no need to access private class data, operator overloading can be done as normal function.
Point operator+(const Point &p, constint n){ returnPoint(p.getX() + n, p.getY() + n); }
Point operator+(int n, const Point &p){ return p + n; }
Point operator+(const Point &p1, const Point &p2){ returnPoint(p1.getX() + p2.getX(), p1.getY() + p2.getY()); }
intmain(int argc, char *argv[]){ Point p1(10, 20); Point p2(4, 5); Point p3 = p1 + p2 + 5;
p3.print();
return0; }
Using Member Function
The assignment (=), subscript ([]), function call (()), and member selection (->) operators must be overloaded as member functions. IO operators(<< and >>) can’t be overloaded as memeber functions.
friend std::ostream& operator<< (std::ostream &out, const Point &p); friend std::istream& operator>> (std::istream &in, Point &p); };
// Overloading as friend function // Returning std::ostream so that it can be chained like std::cout << p1 << p2 std::ostream& operator<< (std::ostream &out, const Point &p){ out << "(" << p.m_x << ", " << p.m_y << ")"; return out; }
std::istream& operator>> (std::istream &in, Point &p){ in >> p.m_x >> p.m_y; return in; }
// Overloading as normal function Point operator+ (Point p, int n){ returnPoint(p.getX() + n, p.getY() + n); }
Point operator+ (int n, Point p){ return p + n; }
Point operator+ (Point p1, Point p2){ returnPoint(p1.getX() + p2.getX(), p1.getY() + p2.getY()); }
intmain(int argc, char *argv[]){ Point p1(10, 20); Point p2;
// () can only be overloaded as member function Point operator()(int n){ returnPoint(m_x + n, m_y + n); }
friend std::ostream& operator<< (std::ostream &out, const Point &p); };
// Overloading as friend function // Returning std::ostream so that it can be chained like std::cout << p1 << p2 std::ostream& operator<< (std::ostream &out, const Point &p){ out << "(" << p.m_x << ", " << p.m_y << ")"; return out; }
intmain(int argc, char *argv[]){ Point pnt(10, 20);
// Will add 5 to pnt.m_x, pnt.m_y and create new Point object. // Notce class object is called like function. std::cout << pnt(5) << "\n";
return0; }
Overloading Typecast
Typecast overloading can be done to convert between types.
// Overloading as friend function // Returning std::ostream so that it can be chained like std::cout << p1 << p2 std::ostream& operator<< (std::ostream &out, const Polar &p){ out << "(" << p.m_r << ", " << p.m_theta << ")"; return out; }
When copy initialization is used a copy constructor is used. By default compiler uses memberwise initialization if no copy constructor is provided. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13
classXyz { private: int m_var; public: Xyz(int x): m_var(x){} };
intmain(void){ Xyz xy(10); // uses default initialization Xyz yz(xy); // Copy initialization // As there is no copy constructor provided, compiler will do a memberwise initialization }
Member functions of a class can access the private members ot the same class type. Here is an example with copy constructor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classXyz { private: int m_var; public: Xyz(int x): m_var(x){}
intmain(int argc, char *argv[]){ Hello h("hello"); Hello g(h); // Copy constructor will be used Hello i = Hello("gello"); // Copy constructor won't be used Hello k(Hello("gello")); // Copy constructor won't be used
Shallow copy means memberwise copying by the compiler if no copy constructor or overloaded assingment operator is provided.
The default copy constructor and default assignment operators do shallow copies, which is fine for classes that contain no dynamically allocated variables.
Classes with dynamically allocated variables need to have a copy constructor and assignment operator that do a deep copy.
Object Relationship
Composition and Aggregation
In both cases relationship between parent and child is ‘has a’.
Composition
The part (member) is part of the object (class)
The part (member) can only belong to one object (class) at a time
The part (member) has its existence managed by the object (class)
The part (member) does not know about the existence of the object (class)
Typically use normal member variables
Can use pointer members if the class handles object allocation/deallocation itself
Responsible for creation/destruction of parts
Aggregation
The part (member) is part of the object (class)
The part (member) can belong to more than one object (class) at a time
The part (member) does not have its existence managed by the object (class)
The part (member) does not know about the existence of the object (class)
Typically use pointer or reference members that point to or reference objects that live outside the scope of the aggregate class
Not responsible for creating/destroying parts
Association
The associated object (member) is otherwise unrelated to the object (class)
The associated object (member) can belong to more than one object (class) at a time
The associated object (member) does not have its existence managed by the object (class)
The associated object (member) may or may not know about the existence of the object (class)
Dependencies
A dependency occurs when one object invokes another object’s functionality in order to accomplish some specific task. This is a weaker relationship than an association, but still, any change to object being depended upon may break functionality in the (dependent) caller. A dependency is always a unidirectional relationship.
Container Classes
Hold and organize multiple instance of another type(class or fundamental type).
std::initializer_list
Used in container class’s constructor for list initialization.
Inheritance
Order of construction
Most base class constructed first and most derived class constructed last.
When a derived class is instanciated following things happen in order:
Memory for derived is set aside (enough for both the Base and Derived portions)
The appropriate Derived constructor is called
The Base object is constructed first using the appropriate Base constructor. If no base constructor is specified, the default constructor will be used.
The initialization list initializes variables
The body of the constructor executes
Control is returned to the caller
Constructors and initialization of derived classes
voididentify(void){ std::cout << "I am base\n"; } };
classDerived: public Base{ public: Derived(){}
voididentify(void){ std::cout << "I am derived\n"; } };
intmain(int argc, char *argv[]){ Derived *pd = newDerived(); // Pointer to base of derived object Base *pb{pd};
// Will call Derived::indentify() pd->identify();
// Will call Base::identify() pb->identify(); return0; }
Use case for this could be in functions which takes derived class as parameter. For each derived class a different functions have to be created. For example:
1 2 3 4 5 6 7
voidreport(Derived &d){ d.identify(); }
voidreport(AnotherDerived &ad){ ad.identify(); }
This problem can be solved using pointer/reference to base:
But the problem is in both cases Base::identify() will be called. As pointer to base only can see memebers from base. This problem can be solved using virtual functions.
Polymorphism
A virtual function is a special type of function that, when called, resolves to the most-derived version of the function that exists between the base and derived class. This capability is known as polymorphism. A derived function is considered a match if it has the same signature (name, parameter types, and whether it is const) and return type as the base version of the function. Such functions are called overrides.
If you want to call functions from base class in virtual function just use scope operator:
1 2 3 4
Derived d; Base *bp = &d;
std::cout << bp->Base::getName() << "\n";
Return type have to match between virtual functions.
Never use virtual function in constructors and destructors.
override and final Specifiers
A derived virtual function is only considered override if functino signature and return type matches exactly between the virtual functions. override specifier enforces virtualization of a function. If signature and return type doesn’t match the compiler will generate an error. If override is used, no need to specify virtual keyword.
Covariant Return Type
Destructors
In case of inheritance destructors should always make virtual. Specially if there is dynamically allocated memory involved. If a derived class is converted to a base class and then call delete, only base destructor will be called. If dynamic memory is allocated in derived class, this will leak memory.
Abstract Class, Pure Virtual Functions and Interface Class
Abstract class is a class wich is only used by the derived class. It can’t be instantiated anywhere else.
A pure virtual function has no body at all. It is meant to be redefined in derived classes. A class with pure virtual functions is an abstract class means it can’t be intantiated. A pure virtual function may or may not have a body.
1
virtualvoiddoSomething()= 0
In interface class has no member variables. All the functions are pure virtual.
Virtual Base Class
Single Base class is shared by the derived classes.
Solves diamond problem.
Most derived class is responsible for constructing the virtual base class.
class PoweredDevice { public: PoweredDevice(int power) { std::cout << "PoweredDevice: " << power << '\n'; } };
class Scanner: virtual public PoweredDevice // note: PoweredDevice is now a virtual base class { public: Scanner(int scanner, int power) : PoweredDevice{ power } // this line is required to create Scanner objects, but ignored in this case { std::cout << "Scanner: " << scanner << '\n'; } };
class Printer: virtual public PoweredDevice // note: PoweredDevice is now a virtual base class { public: Printer(int printer, int power) : PoweredDevice{ power } // this line is required to create Printer objects, but ignored in this case { std::cout << "Printer: " << printer << '\n'; } };
class Copier: public Scanner, public Printer { public: Copier(int scanner, int printer, int power) : PoweredDevice{ power }, // PoweredDevice is constructed here Scanner{ scanner, power }, Printer{ printer, power } { } };
Object Slicing
1 2 3 4 5 6 7 8 9 10
Derived d; Base b(d); // b will get the Base part from d. It is called object slicing
// Will call Base::doSomething() even if it is a virtual function b.doSomething()
Base &b(d);
// Will call Derived::doSomething if it is a virtual function as b in reference to d bp->doSomething()
In general avoid slicing
std::reference_wrapper
Dynamic Casting
Dynamic casing is used for downcasting.
Workaround for Friend Functions
Only member functions can be virtualized. So friend functions can’t be virtualized. For example:
#include<iostream> classBase { public: Base() {} // Here's our overloaded operator<< friend std::ostream& operator<<(std::ostream &out, const Base &b) { // Delegate printing responsibility for printing to member function print() return b.print(out); } // We'll rely on member function print() to do the actual printing // Because print is a normal member function, it can be virtualized virtual std::ostream& print(std::ostream& out)const { out << "Base"; return out; } }; classDerived : public Base { public: Derived() {} // Here's our override print function to handle the Derived case virtual std::ostream& print(std::ostream& out)constoverride { out << "Derived"; return out; } }; intmain() { Base b; std::cout << b << '\n'; Derived d; std::cout << d << '\n'; // note that this works even with no operator<< that explicitly handles Derived objects Base &bref = d; std::cout << bref << '\n'; return0; }
Templates
Function Template
1 2 3 4 5 6 7 8 9 10 11 12 13
#include<iostream>
template <typename T> T add(T x, T y){ return x + y; }
#include<iostream> template <classT, int size> // size is the non-type parameter classStaticArray { private: // The non-type parameter controls the size of the array T m_array[size]; public: T* getArray(); T& operator[](int index) { return m_array[index]; } }; // Showing how a function for a class with a non-type parameter is defined outside of the class template <classT, int size> T* StaticArray<T, size>::getArray() { return m_array; } intmain() { // declare an integer array with room for 12 integers StaticArray<int, 12> intArray; // Fill it up in order, then print it backwards for (int count=0; count < 12; ++count) intArray[count] = count; for (int count=11; count >= 0; --count) std::cout << intArray[count] << " "; std::cout << '\n'; // declare a double buffer with room for 4 doubles StaticArray<double, 4> doubleArray; for (int count=0; count < 4; ++count) doubleArray[count] = 4.4 + 0.1*count; for (int count=0; count < 4; ++count) std::cout << doubleArray[count] << ' '; return0; }
Template Specialization
If an exception is needed to make for an specific type.