If you find the information in this page useful and want to show your support, you can make a donation
Use PayPal
This will help me create more stuff and fix the existent content...
sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update sudo apt install gcc-11 g++-11 gcc-11 --version
// g++-11 main.cpp -o main && ./main #include <iostream> int main() { std::cout << "Hello World C++!\n"; }
PNAME="p1testcpp"; cd .; mkdir -p $PNAME/src;
printf '#include <iostream>\nusing namespace std;\n\nint main(){\ncout << "Hello Template" << endl;\nreturn 0;\n}\n' > $PNAME/src/main.cpp
g++ -Wall -std=c++14 main.cpp -o main && ./main
TARGET=main.out
SRCS:=main.cpp
CC = g++-11
CFLAGS = -Wall -Werror
OBJS = $(SRCS:.cpp=.o)
%.o : %.cpp
$(CC) -c $< $(CFLAGS) -o $@
.PHONY: all
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)
.PHONY: clean
clean:
rm -rf $(OBJS) $(TARGET)
.PHONY: test
test: clean all
./$(TARGET)
C++ is a general purpose programming language with a bias towards systems programming which supports the following coding paradigms:
C++ is used to write code at all levels, including firmware, operating systems, and large-scale applications.
char
type is used to represent the numeric values for character data as represented by the basic character set present on a particular computer. This is determined by the locale settings.wchar_t
type is used which expands on the numeric values available to represent character sets from various languages found around the world (E.g a character data that doesn't fit in the standard ASCII character set, such as Japanese kanji)#include <climits>
library for size and precision when declaring primitive data typesenum Month { January = 1, February, March, April, May, June, July, August, September, October, November, December };
const
& constexpr
std::string
class data
pointer and n
for the number of lements (size()
returns n
)std:string
objects can be used like local variables std::string h{"Hello"};
std::vector
classstd::string
but can store data of any typesize()
returns number of elements and push_back()
add element at the end{}
and parenthesis ()
#include <iostream> #include <climits> #include <vector> //typedef std::vector<int> IntVec; // alias old C and C++ using IntVec = std::vector<int>;// Modern C++ class MyClass {}; int main() { int myVar = 0; int yourVar{1}; bool flag{ true }; int zeroVar{}; double myDouble(53.53); vector<int> vec{1,2,3,4,5,6}; int intArray[] {1,2,4,6}; string s{"My String"}; // sames as: string s("My String"); std::vector<int> old_one(4); // std::vector variable with elements 0, 0, 0, 0 std::vector<int> old_two(4, 2); // std::vector variable with elements 2, 2, 2, 2 std::vector<int> uni_one{4}; // std::vector variable with elements 4 std::vector<int> uni_two{4, 2}; // std::vector variable with elements 4, 2 std::vector<IntVec> vec_of_vec; MyClass mc{}; // removes ambiguity, it doesnt look like a func declaration bool a = true; char b = 'b'; signed char c = -1; signed char d = 64; short e = -16384; unsigned short f = 16384; int g = -2; unsigned int h = 32768; long int i = -32768; long unsigned int j = -32768; long long int k = -4294967296; long long unsigned int l = 4294967296; float m = 3.141615; double n = 3.141615; const char *str = "Hello"; // Equivalent to const char str[] = {'H','e','l','l','o','\0'} std::cout << "bool " << a << " sizeof " << sizeof(a) << "\n"; std::cout << "char " << b << " sizeof " << sizeof(b) << "\n"; std::cout << "signed char " << c << " sizeof " << sizeof(c) << "\n"; std::cout << "signed char " << d << " sizeof " << sizeof(d) << "\n"; std::cout << "short " << e << " sizeof " << sizeof(e) << "\n"; std::cout << "unsigned short " << f << " sizeof " << sizeof(f) << "\n"; std::cout << "int " << g << " sizeof " << sizeof(g) << "\n"; std::cout << "unsigned int " << h << " sizeof " << sizeof(h) << "\n"; std::cout << "long int " << i << " sizeof " << sizeof(i) << "\n"; std::cout << "long unsigned int " << j << " sizeof " << sizeof(j) << "\n"; std::cout << "long long int " << k << " sizeof " << sizeof(k) << "\n"; std::cout << "long long unsigned int " << l << "sizeof " << sizeof(l) << "\n"; std::cout << "float " << m << " sizeof " << sizeof(m) << "\n"; std::cout << "double " << n << " sizeof " << sizeof(n) << "\n"; //std::cout << "Square auto func " << square(g) << "\n"; std::cout << "char MIN " << CHAR_MIN << "\n"; std::cout << "int MIN " << INT_MIN << "\n"; std::cout << "short MIN " << SHRT_MIN << "\n"; std::cout << "long MIN " << LONG_MIN << "\n"; std::cout << "long long MIN " << LLONG_MIN << "\n"; std::cout << "char MAX " << CHAR_MAX << "\n"; std::cout << "int MAX " << INT_MAX << "\n"; std::cout << "short MAX " << SHRT_MAX << "\n"; std::cout << "long MAX " << LONG_MAX << "\n"; std::cout << "long long MAX " << LLONG_MAX << "\n"; // The comma operator has left-to-right associativity. It evaluates the left expression, // discards its result, and returns the right expression int x = (g++, 6); std::cout << "Comma operator test x=" << x << " g=" << g << "\n"; /* Comma operator test x=6 g=-1 */ // C++20 three-way comparison operator <=> a.k.a. 'spaceship operator' // operator compares alphabetical order like strcmp // operator returns an object that can be directly compared with a positive, 0, or negative integer value std::cout << "Spaceship operator test " << ((3 <=> 5) == 0) << "\n"; std::cout << "Spaceship operator test " << ((3 <=> 5) < 0) << "\n"; std::cout << "Spaceship operator test " << ((3 <=> 5) > 0) << "\n"; /* Spaceship operator test 0 Spaceship operator test 1 Spaceship operator test 0 */ // C++20 introduces a set of safe compare functions <utility> // bool cmp_equal(T1 a, T2 b) // bool cmp_not_equal(T1 a, T2 b) // bool cmp_less(T1 a, T2 b) // bool cmp_greater(T1 a, T2 b) // bool cmp_less_equal(T1 a, T2 b) // bool cmp_greater_equal(T1 a, T2 b) unsigned o = 4; int p = -3; std::cout << "Without utility " << (o > p) << " With utility " << std::cmp_greater(o,p) << "\n"; /* Without utility 0 With utility 1 */ }
if (auto iter = begin(vec) ; iter != end(vec))
varible is local to if-else statementswitch (char c = arr[i]; c)
f
and UL
to change thisconst char *cca="Hello"
is null terminated array of const char which has limited functionality, for pure C++ string
objects are recommendedR
and ()
if string has ()
needs an extra delimiter. Examples:std::string url = R"(<a href="file">C:\Program Files\</a>\n)";
std::string delimited_with_x_url = R"x(<a href="file">C:\"Program Files (x86)"\</a>\n)x";
A namespace is a "scope container" where you can place your classes, variables, or other identifiers to prevent conflicts with others.
Anytime you want to reduce ambiguity or name collisions, you use the namespace designator ahead of the identifier.
E.g. using namespace MyNameSpace
(Avoid using the this using namesoace
in headers as it will then be included whenever the header is included and will highly likely result in name clashes)
The ::
is the scope resolution operator and allows you gain access to classes, variables, or other identifiers in the std
namespace. E.g. std::cout
means the cout
object exists in the namespace std
You can define your own namespace by using the keyword namespace and nested namespaces if you choose to provide more separation of identifiers in your code. Namespaces can be combined to form new namespaces as well
namespace MyNameSpace { namespace Geometry { const double PI = 3.14159; double Area(double r) { return PI*(r*r); } } } //Combined namespace namespace MyNameSpace_std { using namespace MyNameSpace; using namespace std; } using namespace MyNameSpace; int main() { double radius = 12.5; double area = Geometry::Area(radius); }
Expression | Description | Example |
---|---|---|
*p++ *(p++) |
Extracts value pointed by p then p is incremented |
*p++ = 1; is equivalent to *p = 1; p++; |
(*p)++ |
The value that p points to is incremented |
(*p)++; is equivalent to *p = *p + 1; |
*++p *(++p) |
First increment p then the new address that p points to now (after the increment) is extracted |
*++p = 1; is equivalent to p++; *p = 1; |
++*p ++(*p) |
Increments the value pointed by p then extract the value pointed by p (evaluates *p ) |
++*p = 1; is equivalent to (*p)++; *p = 1; |
sizeof
on a class will return the sum of all the members/attributes (the size of the methods is omitted for this calculation)struct
in C++ is the same as class, except all the members are public by defaultWhen you call a member function (of an object), the object is passed by address in a hidden argument called this
which you can deferecne to access the members of the object
class Test { int i; std::string s; public: func(int a, float b, std::string s); }; void Test::fun(int a, float b, std::string s) { this->i = 1; // 'this' is equal to &test (the address of test) } Test test; test.func(1, 2.0, "Three"); // Is called as Test::func(&test, 1, 2.0,"Three");
Attributes in an object can also be initialized using the brackets initializer
{}
, empty braces will call the default constructor, if defined. E.g.class Rectangle { public: int w; int h; }; Rectangle myRec{10, 7}; Rectangle otherRec{};
Constructor of astatic
object is called only once when the program execution reaches for the first time the place where the object is defined
A Copy Constructor is used to make a copy of an existing object instance. If you do not declare a copy constructor the compiler will generate one for you automatically.
The compiler generated one is only capable of a shallow member copy, If you have pointers to memory locations in your class you will need to create a copy constructor to copy the data pointed by these pointers correctly/explicitly
There are 4 possible function signatures for a copy constructor.
MyClass(const MyClass& other);
This is the most common to avoid changing something on the object you are copyingMyClass(MyClass& other);
MyClass(volatile const MyClass& other);
MyClass(volatile MyClass& other);
class Test {
int i;
std::string s;
public:
func(int a, float b, std::string s);
};
Test(const Test& other) : i(other.i), str(other.str) {
}
=
with objects)=
is used in the initialization of an object, implicitly the compiler will use the copy constructor in case the equals operator (=
) is not overloaded=
to assign object we must overload the operator accordingly or explicitly implement the copy constructor with our code.=
operator).hpp: MyClass& operator =(const MyClass &rhs); .cpp: MyClass& MyClass::operator =(const MyClass &rhs)
class Test {
int i;
std::string s;
public:
func(int a, float b, std::string s);
};
Test& operator=(const Test& other_on_the_right_side) {
i = other.i;
str = other.str;
return *this;
}
// file: person.hpp #include <iostream> class Person { public: Person(std::string name, unsigned int age); // constructor void print_person_info(void); ~Person(); // destructor private: std::string name; unsigned int age; }; // file: person.cpp #include "person.hpp" Person::Person(std::string name, unsigned int age) { this->name = name; this->age = age; } void Person::print_person_info(void) { std::cout << "Person name: " << this->name << " age: " << this->age << std::endl; } Person::~Person() { std::cout << this->name << " Person Object Destructor" << std::endl; }
They are like pointers because they actually modify the memory of the variable/object they are referring
It can be thought as an alias, that is, another name for an already existing variable.
When a variable is declared as reference, it becomes an alternative name for an existing variable
They are like constant pointer that is automatically dereferenced when you use it
Mostly used for
A variable can be declared as reference by putting &
in the declaration
Differences with pointers:
void
but a reference can never be void
NULL
. A reference must be initialized to a variable when declaredPass by const reference
// file: main.cpp // g++ -std=c++11 -Wall ./main.cpp -o test #include <iostream> #include <memory> // Using Pointers void swap_ptr(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } // Using C++ References // when called references 'a' and 'b' are created and refer to the variables passed as parameter // so 'a' and 'b can modify those variables which are defined outside the scope of this function void swap_ref(int& a, int& b) { int temp = a; a = b; b = temp; } int main(int argc, char *argv[]) { int x = 7, y = 17; swap_ref(x, y); // with references swap_ptr(&x, &y); // with pointers vector<string> names {"anna", "ben", "camila"}; for (auto const &str:names) cout << str << "\n"; // no change for (auto &str:names) str = "noname"; // change }
const
Objectsconst
objects CAN'T make a call to their methods unless these are defined as const
as well (E.g. int get_area () const { return w*h; }
const
functions defined inside a const
object cannot modify the object eitherget
functions as const
const
const
functions can be overloaded with non-const
functions and the one called will depend of the object that is calling it,
if the object is const
the const
function version will be called if not the non-const
version will be called insteadconst
pointers tableDeclaration | Case | Description |
---|---|---|
int* ptr |
No const pointer and No const data |
The pointer can be modified to point to other data and the data that it points to can be modified |
const int* ptr |
No const pointer and const data |
The pointer can be modified to point to other data but the data that it points to CAN'T be modified using the pointer |
int* const ptr = &x |
const pointer and No const data |
The pointer can only point to the value it was initialized, we CAN'T change it to point to different data but we can modify the data using the pointer |
const int* const ptr = &x |
const pointer and const data |
The pointer can only point to the value it was initialized and the data that it points to CAN'T be modified using the pointer either |
void display(const vector<string> *const v) { // (*v).at(0) = "Name"; //const to prevent this for (auto e: *v) { cout << e << ""; cout << endl; } //v = nullptr; //*const to prevent this }
:
operator in constructors)const
members// file: student.cpp ... // Base class constructor (Person in this case) must be called from the initialization list // RACE is a const member Student::Student(std::string name, unsigned int age, std::string school) : Person(name, age), RACE("human") { this->school = school; } ...
:
A class can define an external function as a friend function. This allows the friend function to access all the members of the class (public and private) but these functions
are non-members, which means they don't receive a this
pointer. Consequently, they must require an explicit parameter to access an object.
Friend functions are declared in the class definition (usually .hpp
) with friend
and then implemented in the .cpp
but without the ClassName::
syntax (because they are not members of the class)
The implementation of friend functions can be in any other place it's desired, if they are implemented in the class that does NOT make them methods of the class
this
pointerthis
, so we can use it to access the object attributes and methodsthis
as argument (except to the static
methods)new
& delete
The new
operator is used to dynamically allocate memory and when used does the following:
The delete
operator is used to free/release allocated memory and when used does the following:
dangling pointer
The pointer variable that points to the released memory will still exist on the stack until the end of it's scopeExample:
Time* timePtr = new Time(1, 1, 1); delete timePtr; int* array = new int[10]; delete [] array; // to delete dynamically allocated array through a pointer we must use [] to delete all elements int *p1 = new int{36}; int *p2 = new int(37);
The OS restricts the amount of memory a program may allocate if try to locate over this resctriction most likely will get a
nullptr
(git a Memory exhaustion failur)
static
class attributes & methods::
operatorstatic
can ONLY access static
attributes, this means it can NOT access non-static
attributesstatic
method does NOT have a this
pointer because this pointer must refer to a specific object of the classstatic
functions cannot be const
(TODO: need to check reason for this).hpp
and initialized in the .cpp
using the ::
operator+
, -
, /
, ++
, *
, etc.MyClass& MyClass::operator=(const MyClass&) { . . . } MyClass obj1, obj2; obj1 = obj2; // equivalent to obj1.operator=(obj2);
non-static
function is needed since they need specific objects to operate on<return type> operator<operator symbols>(<parameters...>) void operator+(int x, MyNumClass& num)
using namespace std; class Point { public: int x, y; Point(int px, int py){x = px; y = py;} }; istream& operator>>(istream &is, Point& p) { is >> p.x >> p.y; return is; } // operator overloading implementend using a Global function ostream& operator<<(ostream & os, const Point& p) { os << "[" << p.x << "," << p.y << "]" << endl; return os; } main() { Point p(7,17); cin >> p; // this is equivalent of calling -> operator>>(cin, p); as it were a regular function cout << p; // this is equivalent of calling -> operator<<(cout, p); }
bool MyClass::operator!(void) { . . . } MyClass obj; if(!obj) // equivalent to if(obj.operator!()) { . . . }
bool operator!(const MyClass& refArg) { . . . } // or bool MyClass::operator!(const MyClass objArg) { . . . } MyClass obj; if(!obj) // equivalent to if(operator!(obj)) { . . . }
bool MyClass::operator>(const MyClass& refObj) { . . . } MyClass obj1, obj2; if(obj1 > obj2) // equivalent to if(obj1.operator>(obj2)) { . . . }
bool operator>(const MyClass& refObj1, const MyClass& refObj2) { . . . } MyClass obj1, obj2; if(obj1 > obj2) // equivalent to if(operator>(obj1, obj2)) { . . . }
new
operatornew
operator in a class to customize memory allocation for example to allocate memory
from a custom memory pool instead of the free store, this means a special memory pool for our application#include <new> #include <iostream> using namespace std; //Allocator class class MyAllocator { public: MyAllocator() {} ~MyAllocator() {} static void* operator new (size_t size); static void operator delete (void *p); }; void* MyAllocator::operator new (size_t size) { void* p = malloc(size); if (p == NULL) { std::bad_alloc exception; throw exception; // Throw bad_alloc. } cout << "MyAllocator::operator new called with size_t " << size << endl; return p; } void MyAllocator::operator delete (void* p) { cout << "MyAllocator::operator delete called. " << endl; delete[] p; } int main() { MyAllocator *p = new MyAllocator; //Create MyAllocator object. delete p; //Delete MyAllocator object. return 0; }
It's a code reutilization technique that allows to create new SW components from existing ones. It allows them to obtain all or certain attributes and methods of an already defined component without the need to duplicate code
base class == super class == parent class
sub class == derivative class == child class
Constructors and destructors are not inherited in the same way as the other methods, they are available to the derivative class but in general their own versions must be implemented
public:
private
protected
class ChildClass : <inheritance_type> ParentClass { . . . }; class MyDerivClass : public MyBaseClass { . . . };
virtual
& non-virtual
functionsnon-virtual
functionsnon-virtual
functions the reference and/or pointer is the one that decides the members/methods to be accessedvirtual
modifier are considered in general non-virtual
functions and when we call them using a pointer or a reference is when Polymorphism can happennon-virtual
functions using an object that DOES NOT inherit from another class (E.g. MyClass Obj; Obj.print()
) Polymorphism CANNOT occurclass BaseClass { public: BaseClass() { ... } void print(void) { ... } }; class DerivClass public: BaseClass{ public: DerivClass() : BaseClass() { ... } void print(void) { ... } }; DerivClass dObj; BaseClass* bPtr = &dObj; bPtr->print(); // if 'print' is NOT virtual, this calls function implemented in the BaseClass
virtual
functionsvirtual
modifier the object that is being referenced in the call is the one that determines which method will be called,
it doesn't matter what type of pointer or reference we are usingvirtual
modifiers are only added in the functions prototypes (in the .hpp
file) not needed in the function implementation (.cpp
file)BaseClassName::FunctionName()
class BaseClass { public: BaseClass() { ... } virtual void print(void) { ... } }; class DerivClass public: BaseClass{ public: DerivClass() : BaseClass() { ... } void print(void) { ... } }; DerivClass dObj; BaseClass* bPtr = &dObj; bPtr->print(); // if 'print' IS virtual, this calls function implemented in the DerivClass
virtual
functionsvirtual
function is a function that is NOT implemented in base class and forces the derivative class to implement itvirtual
functions can or cannot be implemented and give the option to the derivative class to overwrite the functionality
or not but pure virtual
functions MUST be implemented in the derivative class or it will produce a compilation errorvirtual
functions is called an abstract class, usually used as the top of a classes hierarchy
to force classes that inherit from it to implemented a certain interface (set of functions)class MyAbsClass { public: MyAbsClass() { ... } virtual void print(void) = 0; // pure virtual function };
virtual
destructorsvirtual
virtual
functions it's destructor should be declared as virtual
(although there could be exceptions)virtual
destructor in the base class, you ensure that the correct destructor is always called when you delete an objectC++ supports multiple inheritance, these means we can inherit form multiple classes and can be implemented with regular inheritance syntax or with templates (not very common to be implemented with templates but achieves equivalent results)
With multiple inheritance it exists the possibility of the diamond problem which can be solved by adding thevirtual
keyword when declaring inheritance of a class to make it explicit that what determines which method will be called is the object being referenced
#include <iostream> using namespace std; class A { public: void printName(void) { cout << "Class A" << endl; } }; // Removing the virtual identifier when declaring inheritance in classes A and B // should cause errors due to calling ambiguity when trying to invoke printName() // It is equivalent to declaring all the methods with the virtual modifier class B: public virtual A { public: void printName(void) { cout << "Class B" << endl; } }; class C: public virtual A { public: void printName(void) { cout << "Class C" << endl; } }; // BC presents the diamond problem because it inherits from B and C // which at the same time each inherits from A creating an inheritance "diamond" class BC: public B, public C { public: void printName(void) { cout << "Class BC" << endl; } }; int main(int argc, char *argv[]) { BC bcObj; bcObj.printName(); // calls printName method from BC class due to virtual inheritance }
<cast_type><variable_type>(variable)
static_cast<type>(variable)
operator.
This is referred as Static Cast and is used to convert from one type to another and the user has to ensure the type cast is safe.
This is usually used to convert a pointer from a base class to a derived class and from a derived class to a base classMyDerived* md = new MyDerived(); MyBase* mb2 = static_cast<MyBase*>(md); // Safe cast since MyDerived Contains MyBase public members from inheritance.
const
modifier to a variable this allows the developer to make a non-modifiable variable
modifiable or a modifiable variable non-modifiable, also to change a variables volatile modifier.
Also sometimes needed to call a method/function that doesnt use const
and you want to pass a const
const int MYCONST = 5; volatile int myvolatile = 9; int* nonconst = const_cast<int*>(&MYCONST); int* nonvolatile = const_cast<int*>(&myvolatile);
UnRelated* urc = new UnRelated(); // Very likely unsafe cast because type UnRelated is not related to MyBase class MyBase* rcast = reinterpret_cast<MyBase*>(urc);
cast
/casting
Operator overloading & Conversion constructorsexplicit
keyword is used to specify a conversion constructor or function cannot be used for implicit conversions
so it is mainly used to declare a conversion constructor can only be used for explicit conversions.MyClass::operator char*() const { . . . } myClass::operator double() { return 3.1416; } MyClass obj; static_cast <char*>(obj); // equivalent to obj.operator char*() static_cast <double>(obj); // equivalent to obj.operator double() or (double) obj; double xd = 2.241234; int xi = static_cast<int>(xd); // equivalent to int xi = (int) xd; in C
Since any constructor with only one argument can be interpreted by the compiler as a conversion constructor and called in certain situations without us being aware we can use the reserved word
explicit
to avoid this situation and stop the the compiler from implicitly calling the constructor
dynamic_cast<DerivClassA*>(basePtr)
if basePtr
points to a DerivClassA
object it returns address of that object (pointer of type DerivClassA
) if not returns 0
/nullptr
instead;Person *basePtr; Employee* dynPtr; basePtr = new Student ("Student", 23, "School"); // if this line is active basePtr points to an 'Student' object //// or //basePtr = new Employee ("Employee", 25, "Company"); // if this line is active basePtr points to an 'Employee' object dynPtr = dynamic_cast<Employee*>(person); // returns '0'/nullprt/NULL if not pointing to 'Employee'/'Student' object if(dynPtr == nullptr) { std::cout << "basePtr is NOT pointing to an Employee/Student object" << std::endl; } else { std::cout << "basePtr IS pointing to an Employee/Student object" << std::endl; std::cout << "You can now access that object through the dynPtr pointer as well" << std::endl;; }
auto
auto
varsauto
is used to let the compiler determine the appropriate type for a variable based on the context of it's initialization (type inference)auto
ignores const and reference qualifier, if a reference is assigned to an auto
variable a copy is created and assigned to the auto
varauto str1 = "Hello"; // equivalent const char str1[] = "Hello" auto str2 = "Hello"s; // equivalent std::string char str1 = "Hello" const int& x{6}; auto y=x; y++; //y==7 x==6 at this point const auto& y = x;
The C++ standard library provides an exception base class that can be derived and allows us to override the what()
method to return a custom error message string and also override it's virtual destructor in case we want to release memory
If you define an ellipsis (...
) as the parameter of an exception handler then it will catch any exception type that is thrown in
its associated try-block. This is most often used as a default exception handler when no other exception handler type matches.
An exception is uncaught if there is no associated exception handler to catch it. When this happens a call to terminate()
is done automatically and any subsequent calls to termination functions that would normally be done during normal program execution does not happen,
this includes calls to allocated objects destructor's functions.
In general it is not considered good practice to throw exception in an objects destructor because it could cause unhandled exceptions. E.g.
if an object is declared inside a try
statement and another exception occurs there, when destroying the object for going out of scope
the destructor will be called and if it throws another exception this will be an unhandled exception
#include <iostream> #include <exception> using namespace std; class derivedexception: public exception { virtual const char* what() const throw() { return "My derived exception"; } } myderivedexception; int main () { try { throw myderivedexception; } catch (derivedexception& e) { cout << e.what() << '\n'; } catch (...) { cout << "All other exception types." << endl; } }
void myfunction() noexcept; //Function does not throw exception, equivalent to noexcept(true) void myfunction() noexcept(true/false); //Throws an exception depending on boolean true/false. true -> does NOT throw, false -> throws void myfunction(void pfa() noexcept); // g takes a pointer to function that doesn't throw void myfunction() throw (); // Old way (before C++11) to specify that throws exception void myfunction() throw (class_name); // Old way (before C++11) to specify that throws exception of specific class usually a class that inherits from `exception`
int x {100}; // 100 is an r-value and x is an l-value int y = x + 100 // (x+100) is an r-value and y is an l-value int m = max(x,y) // max(x,7) is an r-value and m is an l-value int square (int &n) { return n*n; } int num {10}; square(num); // OK square(5); // Error can't reference rvalue 5
T& lvalue;
lvalue reference
T&& rvalue;
rvalue reference
prvalue:
The C++ library provides a template function called move
. Which works more like a type cast.
It's used to tell the compiler to use an object as if it were an rvalue, which means that it makes it moveable.
template<typename T>
or template<class T>
to define a template function or class where T
specifies the type that will be varied, using either option is equivalent but the template<class T>
is usually used for classes just for clarity, to the compiler they mean the samedoubles
and floats
can only be passed as pointers and not by value as defined by the C++ standardTemplates are determined at compile time, so actually the compiler is generating all the necessary code and including it for us
#include <iostream> #include <vector> #include <list> #include <map> #include <iterator> using namespace std; const double pi = 3.1415926535897; class Circle { public: Circle(int rad) : radius(rad) { area = pi*radius*radius; } const bool operator < (const Circle &r) const { return (area < r.area); } friend std::ostream& operator<< (std::ostream& stream, const Circle& cir) { stream << cir.area << "\n"; return stream; } private: int radius; double area; }; template<typename T> void printTypeName(T x) { cout << typeid(x).name() << endl; } template<typename TContainer> void bubbleSort(TContainer begin, TContainer end) { TContainer i, j; for ( i = begin; i != end; i++) { //Loop around all elements in container. for ( j = begin; j != i; j++) { //Loop around container comparing current element to all others and bubble up. if (*i < *j) { //Swap elements. std::swap(*i, *j); } } } } int main() { int myint = 5; printTypeName<int>(myint); printTypeName<>(myint); printTypeName(myint); Circle c(7); printTypeName(c); vector<Circle> circleVec; circleVec.push_back(Circle(5)); circleVec.push_back(Circle(4)); circleVec.push_back(Circle(3)); circleVec.push_back(Circle(2)); circleVec.push_back(Circle(1)); bubbleSort(circleVec.begin(), circleVec.end()); for (vector<Circle>::iterator it = circleVec.begin(); it != circleVec.end(); it++) { cout << *it; } return 0; }
#include <iostream> using namespace std; template<class T = int> class Point{ public: Point() {} Point(T x, T y): px(x), py(y) {} private: T px; T py; }; template<class T1, class T2, int size = 1> class MultiPoint{ public: Point() {} Point(T1 x, T2 y): px(x), py(y), s(size) {} private: T1 px; T2 py; int s; }; int main() { MultiPoint<int, double, 3> myPoint(1, 3.1415161); }
Normally when we write a template class or function we want to use it with many different types, however sometimes we want to code a function or class to make use of a particular type more efficiently this is when we use a template specialization.
To define a specialized template we leave the parameters empty like template<>
this is used if we define a template function
but then we want to have an overloaded "specialized" function for an specific type
// Template function for most cases template<typename T> void printFunction(T arg) { cout << "printFunction arg is type " << typeid(arg).name() << " with value " << arg << endl; } // Specialized template for integers (for type 'int') template<> void printFunction(int intarg) { cout << "printFunction specialization with int arg only called with type " << typeid(intarg).name() << " with value " << intarg << endl; } // Template class for most cases template<class T> class MyClass { public: MyClass(T initVal) { myMember = initVal; cout <<"My Class template " << myMember << endl;} private: T myMember; }; // Specialized Template class for integers (for type 'int') template<> class MyClass <int> { public: MyClass(int initVal) { myMember = initVal; cout <<"My Class specialized template " << myMember << endl;} private: int myMember; };
A container class is used to hold and organize objects of other classes. The containers are implemented as template classes
which allows to be used with many different types. E.g. a vector
is a container and can be:
Sequence containers are named so because their elements are accessed sequentially, they are vector
, list
, deque
and array
, forward_list
(added in C++11)
Associative containers which are designed to provide efficient access to elements by key for example: set
, map
, multiset
and multimap
. by default they are ordered but there is an unordered version for each one (unordered_set
, unordered_map
, unordered_multimap
, unordered_multiset
)
map
and multimap
use key-value pairs and in set
and multiset
the value is the key. Keys must always be unique in all casesAn iterator is an object that allows the traversal of a container
We use the form <type>::iterator
to declare an interator where type
is the typo of the container
They are basically pointers so we use the *
to derefence current element pointed by an interator
We use the begin()
& end()
methods of a container to get an iterator to the first element and to iterate we use a while
loop comparing if begins is less/diffrent from end and increment begin
Types of iterators in C++:
Iterators are used to implement generic algorithms, usually defined as a template function and if they change the elements in the container they will usually define an input and output iterator and copy the input iterator elements to the output iterator to avoid changing the original elements.
#include <iostream> #include <vector> #include <list> using namespace std; template<class TypeRandIter> void randomise(TypeRandIter iterBegin, TypeRandIter iterEnd) { while (iterBegin < iterEnd) { iter_swap(iterBegin, iterBegin + rand() % (iterEnd - iterBegin)); //randomInteger(iterEnd - iterBegin)); ++iterBegin; } } int main(int argc,char* argv[]) { string str = "Hello"; for (string::iterator itStr = str.begin(); itStr != str.end(); itStr++){ cout << *itStr << ","; } cout << "\n\n"; list<int> List = { 34, 77, 16, 2, 35, 76, 18, 2 }; cout << "Calling sort on vector " << "\n"; List.sort(); for (auto it: List) { cout << it << " "; } cout << "\n\n"; cout << "Results after calling randomise are " << "\n"; vector<int> temp(List.begin(), List.end()); // Using constructor that basically builds a new vector of ints randomise(temp.begin(),temp.end()); for (vector<int>::iterator it = temp.begin(); it != temp.end(); it++) { cout << *it << " "; } cout << "\n\n"; cout << "Print orig list after randomize " << "\n"; for (auto it: List) { cout << it << " "; } }
cbegin()
, cend()
, crbegin()
& crend()
return const iteratros and can be used with auto
begin()
and end()
global functions#include <iostream> using namespace std; int main(int argc,char* argv[]) { string str = "Hello"; // const iterator string::const_iterator cit; for (cit=str.begin(); cit!=str.end(); ++cit) cout << *cit << ","; cout << "\n\n"; // reverse iterator string::reverse_iterator rit; for (rit=str.rbegin(); rit!=str.rend(); ++rit) cout << *rit << ","; cout << "\n\n"; // cbegin cend for (auto it=str.cbegin(); it!=str.cend(); ++it) cout << *it << ","; cout << "\n\n"; // global begin end for (auto it=begin(str); it!=end(str); ++it) cout << *it << ","; cout << "\n\n"; }
auto&
for (auto e: str) cout << e << ","; // cannot modify `str`. `e` is a copy of the element cout << "\n\n"; for (auto& e: str){ e++; // can modify `str`. `e` is a reference to the element cout << e << ","; } cout << "\n\n";
auto second = begin(str)+1; // it to seconds element
auto last = end(str)-1; // it to last element
auto mid = begin(str)+(end(str)-begin(str))/2; // it to mid element since end(str)-begin(str) gives the number of elements which can be divided 2
next
takes an interator returns following iterator: auto second = next(begin(str));
prev
takes an interator returns previous iterator: auto last = prev(end(str));
distance
returns number of steps to go from first to second argument distance(begin(str),end(str)); // Returns number of elements
advance
moves an iterator by its second argument auto mid = being(str); advance(mid,distance(begin(str),end(str))/2); // Returns number of elements
it >= begin()
and it < end()
must be true but it==end()
is not allowedAn Interface is used to define the behavior of classes without saying how to implement it. In C++ usually implemented using abstract classes. They contain generalizations about the characteristics of related entities
The Command Pattern is considered a behavioral design pattern, It is like an object based version of a call back in which the requester sends a request as an object. Used to connect two object so a call in one object triggers code in another
A Function Object (also known as functors) is an object that behaves like a function meaning it is callable but since it's essentially and object it can also store data. To be callable it defines the call operator (operator()
)
The Handle-Body pattern allows developers to put implementation (body) and interface (handle) into two classes, then a client uses the handle class to interact with the body class
Nested classes are classes declared inside classes, a nested class is a member of the enclosing class and has the same access rights as other members of the class. This means we can access the members of the enclosing class from the nested class. The enclosing class has no special access to the members of the nested classes
In the Association-Delegation pattern objects of one class are connected to another class in this association the controlling class does not manage the life cycle of the controlled class, it just uses the controlled class or in other words delegates a request to the controlled object however this controlled object can exist without the controlling object.
The Proxy pattern is used to communicate with another object that for some reason cannot be communicated with directly, the proxy is placed in between the client and the final destination for the communication.
A Member Function Pointer holds the offset of the function address in the objects list of functions, while a normal function pointer holds the address of the function to call. If it is a virtual function then it is an offset into the virtual table of function pointers.
return_type (className::*memberFunctionPtrName)(parameter_types) = &className::member_function_name;
while a normal function pointer: return_type (*FunctionPtrName)(parameter_types) = &function_name;
class Base { public: virtual void sayHello(string s) { cout << "Hello from Base class" << s << endl; } }; class Derived : public Base { public: virtual void sayHello(string s) { cout << "Hello from Derived class" << s << endl; } }; ... void (Base::*baseFuncPtr)(string) = &Base::sayHello; void (Derived::*derivedFuncPtr)(string) = &Derived::sayHello; Base baseObj; Derived derObj; string str = "My String!"; (baseObj.*baseFuncPtr)(str); // call Base::sayHello (derObj.*derivedFuncPtr)(str); // call Derived::sayHello (derObj.*baseFuncPtr)(str); // call Derived::sayHello using base pointer and derived object.
#include <iostream> #include <string> using namespace std; // .hpp class Singleton { public: static Singleton* getSingleton() { if (!instance) { // Only allow one instance of class to be generated. instance = new Singleton; } return instance; } void sayHello(string str) { cout << "Hello from singleton \n" << str; } private: Singleton() {} //Private so cannot be invoked. Singleton& operator=(Singleton const& rhs) {} //Private so cannot be invoked. Singleton(Singleton const& ) {}; static Singleton* instance; }; //.cpp Init Static member instance; Singleton* Singleton::instance = NULL; // main.cpp int main(int argc, char* argv[]) { //Get instance create single object. Singleton::getSingleton()->sayHello("Hello world!!! \n"); return 0; }
std::shared_ptr
std::shared_ptr
is a smart pointer that retains shared ownership of an object through a pointer.
They wrap a raw pointer in a class so that when the class object is destroyed the destructor of the smart pointer object could also release the memory pointed by the raw pointer in case no on else is holding a reference to this memory, this helps avoiding memory leaks and allows smart pointers to be used for garbage collection.
Although they are objects, the std::shared_ptr
class defines the operator*
and operator->
to overload the dereference (*
) and indirection (->
) operators, so we can use smart pointer objects as regular pointers
class Test { public: Test(int a = 0 ) : m_a(a) {} ~Test() { cout << "Calling destructor for Object with address: " << this << " with value " << m_a << endl; } public: int m_a; }; shared_ptr<Test> sptr(new Test(5)); cout<< "Count of sptr before other reference assigment " << sptr.use_count() << " " << sptr->m_a << endl; { shared_ptr<Test> optr = sptr; cout<< "Count of sptr after other reference assigment " << sptr.use_count() << " " << optr->m_a << endl; // optr->m_a to prove optr points to the same object than sptr } cout<< "Count of sptr after other reference goes out of scope " << sptr.use_count() << " " << sptr->m_a << endl;
Streams are essentially a sequence of bytes that are sent from device to device
C++ defines a standard iostream library because input/output operations in C++ occur in streams
cin
, cout
and cerr
are predefined objects in the std
namespace
cin
is an instance of the istream
class, and allows you to perform input from the stdin
device.cout
is an instance of the ostream
class, and allows you to perform output to the stdout
device.cerr
is another instance of the ostream
class, and allows you to perform output to the stderr device.istream
and ostream
aren't actually classes in their own right, they are typedefs (i.e. aliases)
that represent character-based instantiations of the basic_istream
and basic_ostream
template classes
std::endl
is a manipulator which tells the stream to output an end-of-line#include <iomanip> // Necessary for parameterized manipulators. #include <iostream> // Necessary for general stream I/O definitions. ... std::cout << std::left << std::fixed << std::setw(8) << -123.45 << std::endl; std::cout << std::scientific << -123.45 << std::endl; std::cout.unsetf(std::ios::fixed | std::ios::scientific);
<fstream>
std::ifstream
inherits from std::istream
, and allows you to read data from a file using the >>
operator.std::ofstream
inherits from std::ostream
, and allows you to write data to a file using the <<
operator.std::fstream
inherits from both std::istream
and std::ostream
, and allows you to read and write data to/from a file using the >>
and <<
operators.std::fstream iofile("file2.dat", std::ios_base::binary | std::ios_base::app);
TODO add lambda example
We use the inline
keyword to define Inline functions which avoid the overhead associated with traditional function calls,
because every time an inline function is called the compiler will insert the body of the function in that location as opposed to making a function call.
However the inline
keyword is a compiler directive that is a recommendation only.
The compiler may ignore your request and compile the function normally resulting in function calls anyway.
Static class members are defined using the keyword static
, if a member is declared static then it is shared by all the objects of the class.
Likewise a static function member is independent of all the objects, they can be accessed using the class name and the scope operator ::
.
Static member functions can only access static data members, static functions and other functions outside the class.
Usually declared in the class definition (.hpp
) e.g. private: static int count;
and initialized in the implementation file (.cpp
) using the scope operator (::
), e.g. int Counter::count = 0;
Ambiguity only leaves work to the compiler to define that ambiguity
the thread_local
modifier was defined as part of the C++11 standard, it declares that a variable is only accessible on the thread
in which it is created. This prevents sharing of the identifier across multiple threads in the same application.
The OS can use various methods to provide memory to an application when it requests more, such as swapping where it uses a portion of storage space on the fixed disk to move data and programs out of main memory and to the local disk, in order to permit a running application to have memory resources
The mutable
keyword is used to declare that a data member can be assigned a value and it can only be applied to non static and non const members.
In general all variables are mutable, an example usage would be if you have a const object but need to be able to modify one of its data members we can declare it mutable and then we are able to change its value
In C++11 we have the alignas
and alignof
keywords, alignas
allows us to align a variable to a multiple number of bytes
(which can make cache read/writes more efficient because when memory is read by the CPU it reads it in as multiples of word size)
and alignof
gives us alignment of a variable in bytes.
In general we cannot use the address operator (&
) on a variable declared with keyword register
because we have told the compiler to keep
the variable in the register and not in the memory. Therefore, there is no memory address for the variable. However using the register
keyword does NOT guarantee the compiler will keep the variable in the register
printf
If you find the information in this page useful and want to show your support, you can make a donation
Use PayPal
This will help me create more stuff and fix the existent content...