Sunteți pe pagina 1din 22

# C++

## General Information

All the statements that begin with a "#" are preprocessor statements. These are
processed before compiling.

## Implementation and Header Files

Header files don't get compiled (unless they are included in an implementation
file).

Every implementation file gets compiled individually into an object file. Then, the
linker takes all the object files and it _glues_ them together into a single file
(_.dll_ or _.exe_).

## Preprocessor Statements

### · _#include_

Includes (literally copy-pastes) all the contents of the parameter file (typically,
a header file) to the current file.

If the file is in an include directory, angular brackets _<>_ can be used.

C standard library files have the _.h_ extension.


C++ standard library files don't have a file extension.

Examples:

```cpp
#include <iostream>
#include "Log.h"
```

### · _#define_

Replaces the keyword with whatever follows the statment.

Examples:

```cpp
#define PI 3.141592
#define INTEGER int
```

### · #if

Include/exclude code based on a given condition.

Examples:

```cpp
#if 1
void foo() {}
#endif
```

### · #pragma once


Only include the file once. It prevents including the same header file multiple
times into a single translation unit. Not all compilers support this, but most do.

The general way of getting this functionality:

```cpp
// Log.h file

#ifndef _LOG_H
#define _LOG_H

// ...
void Log(const char* message);
// ...

#endif
```

## The main function

The main function doesn't need an explicit return statement, by default it returns
_0_.

```cpp
int main() {}
```

```cpp
int main(int argc, int* argv[]) {}
```

## Hello World

```cpp
#include <iostream>

int main() {
std::cout << "Hello, world!" << std::endl;
std::cin.get();
}
```

## [Data Types](https://en.cppreference.com/w/cpp/language/types)

If primitive type variables are not initialized, they will contain whatever was
left in their memory location.

### Character Types

- char : 1 byte
- wchar_t : 4 bytes (Win), 8 bytes (Linux)
- char8_t : 1 byte (UTF-8)
- char16_t : 4 bytes (UTF-16)
- char32_t : 8 bytes (UTF-32)
```cpp
char x = 5;
char c = 'w';
```

### Integer Types

By default, integer types are signed.

- short (at least 2 bytes)


- int (at least 2 bytes, but almost guaranteed to have 4 bytes)
- long (at least 4 bytes)
- long long (at least 8 bytes)

```cpp
short s(376);
int n{-13};
unsigned int n = 42;
long long = 1337;
```

### Decimal Types

- float (4 bytes)
- double (8 bytes)
- long double (10 bytes)

```cpp
float v = 5.3f;
double d = 9.33156;
```

### Boolean Type (1 byte)

```cpp
bool f = false; // 0
bool t = true; // anything other than 0
```

## Pointers

```cpp
int var = 8;
int* ptr = &var; // reference
*ptr = 10; // dereference

int *x, *y; // multiple pointers declared in a single line


```

```cpp
char* buffer = new char[8]; // allocate an 8 byte buffer
memset(buffer, 0, 8); // fill the buffer with zeroes
char** ptr = &buffer; // pointer to a pointer
delete[] buffer; // deallocate
```

## References
A reference variable is just an alias. At compile time, only the original will be
kept.

```cpp
int x = 5;
int& ref = x;
```

References are just syntactic sugar for pointers.

```cpp
void Increment(int& val) {
val++;
}

void AlsoIncrement(int* val) {


(*val)++;
}

int main() {
int x = 5;
Increment(x);
Log(x); // 6
AlsoIncrement(&x);
Log(x); // 7
}
```

Once declared, references cannot be reassigned.

```cpp
int a = 5;
int b = 8;

int& ref = a;
ref = b; // this doesn't change what 'ref' refers to, it will set 'a' to the value
of 'b' (8).
```

## Arrays

```cpp
int values[5]; // allocate memory to store 5 integers
values[2] = 87; // set the 3rd element
int third = values[2];
```

Because arrays are just pointers to the first element in the array, we can do the
following:

```cpp
int values[3] = { 0, 1, 2 }; // array with initialized values
int* ptr = values;
int third = values[2];
int alsoThird = *(ptr + 2); // add a 2*sizeof(int) byte offset
int otherThird = *(int*)((char*)ptr + 8); // to set an offset byte by byte, cast
the pointer to a single-byte type
```

The size (element count) of stack allocated arrays can be calculated.

```cpp
int values[5];
int count = sizeof(values) / sizeof(int); // 5
```

But is better to maintain the size in a separated variable.

```cpp
class Entity {
static const int data_size = 5;
int data[data_size];
}
```

### Arrays stored in the heap

Arrays have to be deleted with the _delete[]_ operator.

```cpp
int* arr = new int[5];
delete[] arr;
```

Arrays decay into pointers, but are not the exact same thing.

```cpp
char s[6] = "Hello";
PrintSomething(s);
PrintSomething(&s[0]); // compiler will treat it like this
// ...
void PrintSomething(char* ptr) {}
```

## Strings

### C-style Strings

String literals are immutable because they are stored in a read-only section of the
memory. They have a fixed size once assigned and are null-terminated.

```cpp
const char* str = "C-style string";
int n = strlen(str); // string length
```

If declared as arrays, an explicit null-termination is required.

Be aware that even if this looks like a normal array, is still an immutable string.
The compiler is creating a new variable with a modified copy of the string.

```cpp
char name[5] = { 'J', 'o', 'h', 'n', '\0' };
name[0] = 'j';
```

### C++ Standard Strings

```cpp
#include <string>
// ...
std::string str = "C++ string";
int n = str.size(); // string length
```

#### Concatenation

Raw strings cannot be concatented because they have a fixed size, but
_std::string_-s have the _+_ operator overloaded to allow so.

```cpp
std::string str1 = "Hello";
str2 += ", world!";
std::string str2 = std::string("Hello") + ", world!";
```

Since C++14:

```cpp
#include <string>
using namespace std::string_literals;
// ...
std::string str1 = "Hello"s + ", world!";
std::wstring str2 = L"Hello"s + L", world!";
```

#### Other _std::string_ functions

```cpp
std::string str = "my string";
bool contains = str.find("ring") != std::string::npos;
```

### Other type of strings

```cpp
const char* name1 = u8"John"; // 1 byte, UTF-8
const wchar_t* name2 = L"John"; // 2 (Win) 4 (Linux) bytes
const char16_t* name3 = u"John"; // 2 bytes, UTF-16
const char32_t* name4 = U"John"; // 4 bytes, UTF-32

std::string str1 = "John";


std::wstring str2 = L"John";
std::u16string str3 = u"John";
std::u32string str3 = U"John";
```

### Multiline strings

```cpp
const char* str1 = "Line1\n"
"Line2\n"
"Line3\n";

const char* str2 = R"(Line1


Line2
Line3
)";
```

## _if_ and _else_ statements

Code of the if body will only execute if the condition value is anything other than
0.

Examples:

```cpp
int x = 5;
bool condition = x == 5;

if (condition)
x *= 2;

// - - -
const char* ptr = "Hello";

if (ptr) {
Log(ptr);
} else { // ptr == nullptr == 0
Log("Error!");
}
```

## _while_ and _for_ loops

```cpp
int i = 0;
while (i < 5) {
Log("Hello!");
i++;
}
```

```cpp
for (int i = 0; i < 5; i++) {
Log("Hello!");
}
```

## _do-while_ loop

```cpp
bool correctNumber = false;
do {
Log("Select a number:");
int x = GetNumber();
if (x == 5) {
correctNumber = true;
}
} while (!correctNumber)
```

## _static_ keyword

### Outside a class/struct

A variable or function with the _static_ keyword is only available in the


translation unit where it's defined (it's like a _private_ keyword for the
translation unit, the linker won't see that symbol in a global scope).

Example:

```cpp
static double E = 2.71;
static void Foo() {}
```

### Inside a class/struct

A static member/method is not associated to an instance of the class.

```cpp
struct Entity {
static int idIncrement = 0;

int id;

Entity() {
id = idIncrement++;
}

static const char* Info() {


return "This is an entity";
}
}
```

### Local Scope

```cpp
int Count() {
static int n = 1;
return n++;
}
```

## _extern_ keyword

A variable with the extern keyword references to a variable in another translation


unit.

```cpp
// main.cpp
extern int myVariable; // reference to myVariable in other.cpp
// - - -
// other.cpp
int myVariable = 5;
```

## _inline_ keyword

When a function with the _inline_ keyword is called, the linker will replace the
function call with the body of the function.

Example:

```cpp
#include <iostream>

inline void Log(const char* string) {


std::cout << string << std::endl;
}

int main() {
Log("Hi!");
}
```

## _sizeof_ keyword

```cpp
size_t charSize = sizeof char;
size_t intSize = sizeof(int);
```

## _const_ keyword

### _const_ in variables

_const_ creates a constant that once assigned cannot be modified. It doesn't affect
at compiling.

```cpp
const double PI = 3.141592; // cannot change the value
```

Using _const_ before the pointer symbol won't allow to modify the contents of the
pointer, but it can be reassigned.

```cpp
const int* a = new int; // 'const int*' or 'int const*' are the same
*a = 2; // compile error
int b = 7;
a = &b; // this is fine
```

If instead it's after the pointer symbol, the pointer cannot be reassigned but its
contents can be modified.
```cpp
int* const a = new int;
*a = 2; // this if fine
a = nullptr; // compile error
```

### _const_ in class methods

A method with the _const_ cannot modify the class members (like a read-only
method).

```cpp
class Entity {
private:
int x_, y_;
int* id_;
public:
int GetX() const {
return x_;
}

void SetX(int x) {
x_ = x;
}

// returns a pointer that cannot have its contents modified


const int* GetId() const {
return id_;
}
}

void ManageEntity(const Entity& e) {


int x = e.GetX(); // can read
e.SetX(2); // cannot modify, compile error
}
```

## _constexprt_ keyword

_constexpr_ creates a constant that has to be known at compile time.

## _mutable_ keyword

### · _mutable_ in a class member

A variable with the _mutable_ keyword can be modified from a _const_ method.

```cpp
class Entity {
private:
int x;
mutable int debug_count = 0;
public:
void GetX() const {
debug_count++;
return x;
}
}
```

### · _mutable_ in a lambda

A _mutable_ lambda can modify variables passed by value. At compiling, it's just
creating a new local variable.

```cpp
int x = 5;

auto f = [=]() mutable {


x++; // x can be modified
// ...
}

// same behavior
auto g = [=]() {
int y = x;
y++;
// ...
}
```

## Classes and Structs

The only difference between classes and structs is that by default, classes have
private visibilty and structs have public visibility.

```cpp
class Player {
private:
int x, y;
int speed;
public:
void Move(int xa, int ya) {
x += xa * speed;
y += ya * speed;
}
};

int main() {
Player player;
player.Move(1, -1);;
}
```

## _this_ keyword

```cpp
class Entity {
private:
int x, y;
public:
Entity(int x, int y) {
Entity* const e = this;

this->x = x;
this->y = y;
}

int GetX() const {


const Entity* const e = this;
return x;
}
}
```

## The arrow (->) operator

The arrow operator is a shortcut for accessing class members or methods on object
pointers.

```cpp
class Example {
public:
int a = 2;

void Print() {
// ...
}
}
```

```cpp
Example* e = new Example;
int x = e->a;
e->Print(); // (*e).Print();
```

## Visibility

- _private_ : can only be accessed from the class where it's declared.
- _protected_ : can only be accessed from the class and all it's subclasses.
- _public_ : can be accessed from everywhere.

## Instantiate Objects

Example object:

```cpp
class Example {
int a_;
public:
Example(int a) : a_(a) {}
}
```

### Allocating on the stack

```cpp
int a = 5;
int arr[10];

Example e1; // default constructor


Example e2(42); // defined constructor
Example e3 = Example();
Example e3 = Example;
Example e4 = Example(42);
Example e5 = 42;
Example e6 = { 42 };
```

### Allocating on the heap

The _new_ keyword allocates memory on the heap, calls the constructor and returns a
pointer to where the object has been allocated.

Allocating on the heap takes longer, because the program has to find a big enough
part of contiguous memory to allocate the object. To free the used memory use the
_delete_ keyword. That will free the memory and call the destructor.

When to allocate on the heap:

- The object is _really big_.


- You want to manage the lifetime of the object.

```cpp
int* a = new int;
*a = 5;
int* arr = new int[10];

Example e1 = new Example;


Example e2 = new Example(42);
// ...
delete a;
delete[] arr;
delete e1;
delete e2;
```

#### Placement _new_

Creating an object in an specific memory part. The provided pointer must have
allocated enough memory for the object to be placed.

```cpp
int* a = new int[50];
Example* e = new(a) Example();
```

## Implicit Conversion

```cpp
class Entity {
private:
std::string name_;
int age_;
public:
Entity(std::string name)
: name_(name), age_(-1) {}
Entity(int age)
: name_("Unknown"), age_(age) {}
}

void PrintEntity(const Entity& e) {


// ...
}
```

```cpp
Entity e1 = "Example";
Entity e2 = 42;

PrintEntity(27);
// This doesn't work, because we are providing a 'const char*',
// not a 'std::string', and it would have to do
// two type conversions (from 'const* char' to 'std::string' and then to 'Entity&')
// PrintEntity("Name");
```

### _explicit_ keyword

A constructor with the _explicit_ keyword won't allow to create an object using
implicit conversion.

```cpp
class Entity {
private:
std::string name_;
int age_;
public:
Entity(std::string name)
: name_(name), age_(-1) {}
explicit Entity(int age)
: name_("Unknown"), age_(age) {}
}
```

```cpp
Entity e1 = 22; // error
Entity e2(22); // ok
```

## Enums

Each enum value is just an integer. By default, the type is a 32 bit integer,
starting from 0.

Enums don't have a namespace, so it's a common practice to add a prefix.

```cpp
enum IpAddr {
V4, // 0
V6 // 1
};

Set a custom integer type:

enum Example : unsigned char {


ExampleA = 2,
ExampleB, // 3
ExampleC, // 4
ExampleD // 5
}

int main() {
IpAddr ip1 = V4;
Example x = ExampleC;
}
```

## Constructor

```cpp
class Entity {
private:
int x_;
int y_;
public:
Entity(int x, int y) {
x_ = x;
y_ = y;
}
};
```

C++ provides a default constructor. To delete it:

```cpp
class Entity {
Entity() = delete;
}
```

### Member Initializer Lists

Member initializers should always be in the same order as they are declared in the
class, because that's the order they are initialized.

```cpp
class Entity {
private:
int x_;
int y_;
public:
Entity(int x, int y)
: x_(x), y_(y) {}
}
```
This is the more efficient way of initializing non-primitive members, because the
member is only assigned once and no default constructor is called.

```cpp
class Entity {
private:
std::string name; // default constructor called
public:
Entity() {
// equivalent to:
// name = std::string("Default");
name = "Default";
}
}
```

## Destructor

Called when the instance gets deleted.

```cpp
class Entity {
~Entity() {}
}
```

## Copy Constructor

By default, C++ creates a copy constructor that copies the class members (a shallow
copy).

```cpp
Entity a = Entity();
Entity b = a; // creating a copy with the copy constructor
```

To avoid unwanted copying, you can delete the copy constructor:

```cpp
class String {
String(const String& other) = delete;
}
```

## Inheritance

```cpp
class Base {};
class Derived : Base {};
```

## Virtual Functions

Using the _virtual_ keyword will generate a v-table for the function, so that if it
gets overriden the correct function is executed.

```cpp
class Base {
virtual void PrintName() {
std::cout << "Base" << std::endl;
}
};

class Derived {
void PrintName() override {
std::cout << "Derived" << std::endl;
}
}

int main() {
Base b = new Derived;
b.PrintName();
}
```

## Interfaces (Pure Virtual Functions)

Subclasses of a superclass with a pure virtual function must implement the virtual
function to be able to instantiate the subclass.

```cpp
#include <string>

class Printable {
virtual std::string GetClassName() = 0;
}

class Entity : Printable {


std::string GetClassName() override { return "Entity"; }
}

class Player : Entity {


std::string GetClassName() override { return "Player"; }
}
```

## Ternary Operator

```cpp
int level = 5;
std::string rank = level > 7 ? "Master" : "Beginner";
```

## Operator Overloading

```cpp
#include <iostream>

struct Vector2 {
float x, y;
Vector2(float x, float y)
: x(x), y(y) {}

Vector2 Add(const Vector2& other) const {


// return *this + other;
return operator+(other);
}

Vector2 operator+(const Vector2& other) const {


return Vector2(x + other.x, y + other.y);
}

Vector2 Multiply(const Vector2& other) const {


return Vector2(x * other.x, y * other.y);
}

Vector2 operator*(const Vector2& other) const {


return Multiply(other);
}

bool operator==(const Vector2& other) const {


return x == other.x && y == other.y;
}

bool operator!=(const Vector2& other) const {


// return x != other.x || y != other.y;
// return !operator==(other);
return !(*this == other);
}
}

std::ostream& operator<<(std::ostream& stream, const Vector2& vector) const {


stream << other.x << ", " << vector.y;
return stream;
}
```

```cpp
Vector2 vec1(5.2, 3.4);
Vector2 vec2(7.1, 8.5);
Vector2 vec3(1.5, 6.4);

Vector2 result1 = vec1.Add(vec2.Multiply(vec3));


Vector2 result2 = vec1 + vec2 * vec3;

if (result1 == result2) {
std::cout << result1 << std::endl;
}
```

## Smart Pointers
```cpp
#include <memory>

class Example {
private:
int a;
public:
Example() : a(0) {}
Example(int a) : a(a) {}

void Print() {
// ...
}
}
```

### · *std::unique_ptr*

The object gets deleted when it goes out of the scope.

```cpp
std::unique_ptr<Example> var = std::make_unique<Example>();
var->Print();
```

### · *std::shared_ptr*

The object gets deleted when all the references to it get deleted. It works by
having a reference count and increasing it everytime the pointer gets copied.

```cpp
std::shared_ptr<Example> e;
{
std::shared_ptr<Example> var = std::make_shared<Example>();
e = var;
}
// 'e' is still valid
```

### · *std::weak_ptr*

Allows copying a *std::shared_ptr* without increasing its reference count.

```cpp
std::weak_ptr<Example> e;
{
std::shared_ptr<Example> var = std::make_shared<Example>();
e = var;
}
// e = nullptr;
```

## Dynamic Arrays

```cpp
#include <vector>
struct Vertex {
float x, y, z;
}

std::ostream& operator<<(std::ostream& stream, const Vertex& vertex) {


stream << vertex.x << ", " << vertex.y << ", " << vertex.z;
return stream;
}
```

```cpp
std::vector<Vertex> vertices;
vertices.reserve(2); // reserve initial memory for 2 vertices
Vertex vert = { 1, 2, 3 };
vertices.push_back(vert); // copy a vertex to the array
vertices.emplace_back(4, 5, 6); // construct a Vertex with these parameters

for (int i = 0; i < vertices.size(); i++)


std::cout << vertices[i] << std::endl;

// Use a reference to avoid copying each vertex


for (Vertex& v : vertices)
std::cout << v << std::endl;

vertices.erase(vertices.begin() + 1); // erase the 2nd element


vertices.clear() // set the array size back to 0
```

## std::tuple, std::pair

```cpp
#include <tuple>
#include <string>

std::pair<std::string, std::string> GetSources() {


std::string source1;
std::string source2;
// ...
return std::make_pair(source1, source2);
}
```

```cpp
auto sources = GetSources();
std::string src1 = std::get<0>(sources);
std::string src2 = std::get<1>(sources);
```

```cpp
auto sources = GetSources();
std::string src1 = sources.first;
std::string src2 = sources.second;
```

## Templates

Templates functions or classes with specific parameters are created at compile


time.

```cpp
#include <iostream>

template<typename T> // 'typename' or 'class' do the same in this case


void Print(T value) {
std::cout << value << std::endl;
}

template<typename T, int N>


class Array {
private:
T array_[n];
public:
int GetSize() const { return N; }
}
```

```cpp
Print<int>(5);
Print(5); // the type can be automatically deduced

Array<int, 5> arr;


```

## Useful Functions

- memset: set _count_ bytes of memory in the _dest_ pointer to the value _ch_.

```cpp
void* memset(void* dest, int ch, std::size_t count);
```

- malloc: allocate _size_ bytes of memory on the heap and return the pointer.

```cpp
void* malloc(size_t size);
```

- free: free the memory at a pointer.

```cpp
void free(void* ptr);
```

## Useful Code Tricks

### Getting the memory offset of a class member

```cpp
struct Vector3 {
float x, y, z;
}
```

To get the offset of each member we are accessing the variables from an invalid
memory pointer (in this case, nullptr or 0), then we get the memory address of that
variable and finally we cast it into an _int_.

```cpp
int offset_x = (int) &((Vector3*) nullptr)->x; // 0
int offset_y = (int) &((Vector3*) nullptr)->y; // 4
int offset_z = (int) &((Vector3*) 0)->z; // 8
```

## Documentation

- [Understanding lvalues and rvalues]


(https://eli.thegreenplace.net/2011/12/15/understanding-lvalues-and-rvalues-in-c-
and-c/)

S-ar putea să vă placă și