C++ programming
The C++ programming language is a middle-level language that is built on top of the C (compatibility), with additional features.
You will create a file with the .cpp
extension, for instance, main.cpp
#include <iostream>
int main() {
std::cout << "Hello World" << std::endl;
}
➡️ You can use .cc
too and .hpp
for headers (see .h or .hpp, .ii, .tpp...).
Then, use g++ to generate an executable of your program.
$ g++ main.cpp -o a.out
Then, run your executable with
$ ./a.out
Hello World
➡️ The current standard of C++ is C++20, C++23 will be released soon, and some famous versions are C++17, C++11, and C++98.
Basics
This assumes that you're already familiar with the C language.
Declare a variable
C++ added constructors on top of what you can do in C.
int a; // implicit default constructor
int a(); // explicit default constructor
int a(0); // explicit constructor
int a = 0; // implicit constructor
int a = int(0); // explicit constructor
Types
bool xxx = false; // new type for booleans
bool yyy = true;
std::string str = "xxx"; // new type for strings
Conversions
Instead of using casting, we usually use:
int xxx = int('c'); // the constructor (if any)
References
Reminder: in C, the parameters of a function are passed by value. We could use pointers to allow a function to edit a variable from the outer scope. In C++, we could use references instead of pointers.
C (using a pointer)
void inc(int* v){ (*v)++; }
int a = 0;
int* b = &a;
inc(b); // a == *b == 1
C++ (using a reference)
void inc(int& x){ x++; }
int a = 0;
int& b = a;
inc(b); // a == b == 1
Constant references cannot be reassigned again, but they work the same as non-constant references.
const int& c = a; // a++ => c++ | c++ => a++
const int& xxx = 42; // created from a value
Print some text in the terminal
#include <iostream>
std::cout << "Hello, World" << std::endl;
// you can use variables
std::string name = "World";
std::cout << "Hello " << name << std::endl;
➡️ Use std::cerr
to print errors. We use std::endl
to add a newline, and flush the buffer (print immediately).
Control-flow structures
...
Exceptions
Exceptions are signals that are sent when something unexpected happens, such as an error (ex: 1/0
).
try {
// ✅ std::exception or a subclass
throw std::exception();
throw std::runtime_error("some message");
// ❌ avoid using a string
throw "xxx";
}
// 👉 you can add a "catch" for each type of exception
catch ( std::exception &e ) { std::cerr << e.what(); }
catch ( const char* msg ) { std::cerr << msg; }
// 👉 catch every kind of exception
catch (...) { std::cerr << "Error: xxx"; }
➡️ The signal is propagated upwards until someone catches it. If no one does, then the program crashes.
Functions
Default values for parameters
If you give one parameter a default value, any following parameter must have a default value.
int& abc(int& a, int b=1, float c=2.0f) {
/* ... */
return a;
}
➡️ Default values are only in the declaration (if any), but not in both the implementation and the declaration.
// prototype with default values
int& abc(int& a, int b=1, float c=2.0f);
// 👉 no default value
int& abc(int& a, int b, float c) {}
Overloading
Overloading (surchage
) means having multiple functions with the same name, but a different signature.
- ❌ The return type DOES NOT matter
- ❌ The name of the arguments DOES NOT matter
int sum(int a, int b); // ✅ - names are optional
float sum(float, float); // ✅
double sum(double, double); // ✅
int sum(int b, int a); // ❌ - same as 1
int sum(float, float); // ❌ - conflict with 2
int sum(int, int, int); // ✅
- ✅ The attribute
const
attached to a function DOES matter, but it's only available for classes or structures.
struct XXX {
int xxx();
int xxx() const; // ✅
};
Namespaces
Namespaces (espaces de noms
) are the same as packages in other languages. For instance, all functions of the STD are in the namespace std::
. This allows us to declare functions/classes/... with the same name as one of the STD without causing conflicts.
namespace xxx {
// declare functions, types, global variables,
// classes, structures...
float yyy = 6;
// nested namespaces
namespace zzz {
float ttt();
}
}
int main() {
float v1 = xxx::yyy;
float v2 = xxx::zzz::ttt();
}
You can import a namespace, it's a bad practice with std::
through.
using xxx::yyy; // import one ✨
using namespace xxx::zzz; // import all 🚀
int main() {
float v1 = yyy;
float v2 = ttt();
}
::
is called the scope operator (opérateur de résolution de portée
).
Structures and Classes: Basics
In C++, structures were enhanced and are now similar to the newly introduced classes, with one exception 🎯: members (attributes and methods) of a structure are public by default, whereas in a class, they are private by default.
We usually use structures for "data classes" (ex: Person), and classes for everything else (ex: XXXManager, XXXParser...).
struct XXX {
private:
int xxx;
public:
XXX() = default;
explicit XXX(int xxx) : xxx(xxx) {}
public:
int yyy() { return xxx++; };
int zzz() const { return xxx; };
};
int main() {
XXX x; // implicit XXX()
std::cout << x.yyy() << "\n"; // 0
std::cout << x.zzz() << "\n"; // 1
const XXX y(10); // explicit XXX(int)
std::cout << y.zzz() << "\n"; // 10
}
Visibility
struct XXX {
public:
/* ... */
public:
/* ... */
private:
/* ... */
}
We can create public, private, and protected groups in which we will declare attributes and methods.
➡️ You can use the same modifier multiple times to make your declaration clean and tidy.
Attributes
See also: attributes.
struct XXX {
private: // usually private
float x;
float y = 1.0; // 👉 default value
static int ZZZ; // 👉 class attribute
static const int TTT = 200; // 👉 class constants
};
// 🎯 to initialize a class attribute,
// you must add after the declaration:
int XXX::ZZZ = 0;
➡️ Attributes without a default value must be initialized in a constructor, unless they have a default constructor.
Methods
See also: methods.
struct XXX {
public:
void aaa(float x_) { x = x_; } // inline getter
float bbb() const { return x; } // inline setter
void ccc(); // external method
inline void ddd(); // inline method
static void eee() { std::cout << XXX::TTT; } // static
};
// implementations
void XXX::ccc() { std::cout << x; }
void XXX::ddd() { std::cout << x; }
➡️ this->x
or x
are the same: both reference the attribute x
.
➡️ Methods must be marked as const
to be called from const
variables. Normal variables can call const
methods too.
➡️ Methods with a body inside the class are implicitly inline
. Inline methods increase the size of the structure, but they are faster.
Constructors
Every class has a public, parameterless constructor. called the default constructor. Explicitly adding a constructor will delete it.
struct XXX {
public:
XXX() : x(0) {} // explicit default constructor
XXX(float x, float y) : x(x), y(y) {}
};
➡️ Note that when entering the body of the constructor, parameters were already initialized once.
🧼 For an empty constructor with no initialization list, use XXX() = default;
instead of an empty body.
🧼 Use explicit
for constructors with one argument, to avoid implicit casting such as:
struct XXX {
XXX(float x) : x(x) {}
};
// implicit XXX(float)
XXX xxx = 1.0;
Destructors
A destructor is automatically called when the object is destroyed. They are used to free/... resources allocated by the constructor.
struct XXX {
~XXX() {}
};
XXX* xxx = new XXX();
delete xxx;
➡️ For an empty destructor, use ~XXX() = default;
.
Structures and Classes: Advanced
6 methods are available by default in every structure/class
- Default, Copy, and Move Constructor1
- Move assignment operator (
opérateur de mouvement
)1 - Copy assignment operator (
opérateur =
)
1 both of them were introduced in C++11.
Copy constructor
The default copy constructor copies every attribute using its copy constructor. You should use = default;
instead of {}
.
struct XXX {
public:
XXX() {}
XXX(const XXX& xxx) {}
};
void f(XXX xxx) {}
XXX xxx; // default
XXX yyy = xxx; // copy
XXX zzz(xxx); // copy
XXX ttt = XXX(xxx); // copy
f(xxx); // copy
Operator overload
Every operator is a function, and we can overload them, aside from: ::
(scope resolution), .
(dot operator), .*
/->*
(pointer to member), :
(ternary operator), sizeof
, typeid
, and every casting operator.
👉 An ideology is to separate operators between
- Internal: modify and returns
*this
- External: generate a new instance
To use an operator, such as +=
, you can do x+=?
or x.operator+=(?)
...
🎯 Note that the return value and the type of the operands is up to you! Only the number of operands is fixed.
Internal operators: ++
, --
, +=
, -=
, *=
, /=
, =
, -
, ()
, []
...
struct XXX {
int x;
explicit XXX(int x = 0) : x(x) {}
public:
// usually returns a value
int operator++(int v) { return x += v; }
int operator--(int v) { return x -= v; }
// ex: XXX yyy = -xxx;
XXX& operator-() {
x = -x;
return *this;
}
// For every other operator
XXX& operator+=( const XXX& other ) {
x += other.x; // code specific to +=
return *this;
}
};
External operators: +
, -
, *
, /
, >
, >=
, <
, <=
, ==
, !=
...
// same code for '-', '*' '/'
XXX operator+( const XXX &a, const XXX &b ) {
return XXX(a.x + b.x);
}
// same code for '>', '>=', '<', '<='
// '||', '&&', '==', '!='
bool operator>( const XXX &a, const XXX &b ) {
return a.x > b.x;
}
➡️ If you declared operators inside a namespace in a header file, you must use ns::operator>
to reference operator>
inside the namespace ns
.
➡️ Note that by default, ||
has a higher priority than &&
, but if you overload the operator, it will lose this priority.
Stream operators: <<
, >>
#include <ostream>
std::ostream& operator<<(std::ostream& os, const XXX& xxx);
std::ostream& operator<<(std::ostream& os, const XXX& xxx) {
os << "XXX{ ";
os << "x=" << xxx.x;
os << " }\n";
return os;
}
std::cout << XXX(5); // XXX{ x=5 }
Abstraction and inheritance
Inheritance (héritage
) is allowing us to extend another class/struct.
- ➡️ Multiple inheritance is possible, but conflicts must be handled
- ➡️ The inheritance modifier determines how child classes behave
Inheritance modifier
- public: no changes
-
protected:
public->protected
-
private:
public->private
,protected->private
struct XXX { public: int xxx; };
struct YYY : public XXX {}; // xxx is public
struct ZZZ : protected XXX {}; // xxx is protected
struct TTT : private XXX {}; // xxx is private
➡️ Inheritance (by default): public
for structs and private
for classes.
➡️ Inside a method, to explicitly access something from the parent such as xxx
in the example above, use XXX::xxx
("super" in Java/...).
➡️ If a class is used in multiple inheritances, you may use YYY : virtual public XXX
if the class extends two classes with the same parent XXX
, to avoid problems with duplicates.
Constructors and Destructors
First, parent constructors are called, then the child one is called.
struct XXX {};
struct YYY {};
struct ZZZ : XXX, YYY { // public inheritance
ZZZ() : XXX(), YYY() {}
};
Destructors are called in the reverse order, from child to parent 🔄.
Virtual methods
Use virtual
to allow the child class to override
the code of a method declared in the parent class.
If the parent calls a virtual
method, and the child override
this method, then the method in the child will be called.
struct Parent {
int x() { return y(); }
virtual int y() { return 0; }
};
struct Child : Parent { int y() override { return 1; } };
// Example
Child xxx;
std::cout << xxx.x(); // 1
std::cout << xxx.Parent::x(); // 1
⚠️ If there is a non-trivial destructor, you must make it virtual!
Liskov Substitution Principle
As a child class inherits everything from its parent, if a method requires the parent class, then we can pass a child class.
Parent xxx = Child(); // we can store Child inside Parent
⚠️ But there is a major problem. Calling any function on xxx
will always call the function inside the parent! To avoid this, the only trick that I know is to use pointers. 🐛
Abstract classes
A class is abstract if there are still abstract methods in it.
struct Parent {
virtual int x() = 0; // abstract method
};
struct Child : Parent {
int x() override { return 0; }
};
👻 To-do 👻
Stuff that I found, but never read/used yet.
- cpp-cheatsheet
- clang
- seleborg (sort of online book, french)
- cppreference
- learncpp
- cppbestpractices
- Cpp-Learning-Materials
- w3schools
- fluentcpp
- onlinegdb
- microsoft
-
(variable.*(&Classe::methode))()
(see SO) -
[]
and()
(examples) - cmath
- nullptr, new, delete
-
getline(cin, string_variable)
/std::cin
/std::cerr
- placement new
- STD+C++11 course
- awesome-cpp
- googlemock
- boost
- jason turner c++
- vcpkg