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
constattached 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