run cpp file on command line (with g++ compiler)
g++ -Wall -std=c++14 textfile.cpp
// - Wall: turn all warnings on
// std=c++14: set standart
// textfile.cpp: set output file
Alternatives for multiple files
g++ -Wall helloworld-helper.cpp helloworld.cpp -o compiled_file
g++ *.cpp -o compiled_file
Afterwards run
./compiled_file
To setup vscode debugger for multiple .cpp files, check that "args" in tasks.json of the workspace config match the following:
Click to expand!
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++ build active file",
"command": "/usr/bin/g++",
"args": [
"-g",
"${fileDirname}/*.cpp",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
}
],
"version": "2.0.0"
}
use following three files:
Click to expand!
- hpp header file numbers.hpp
// numbers.hpp
#pragma once
// To make sure you don't declare the function more than once by including the header multiple times.
// alternatively use
// ifndef HEADERVAR
// #define HEADERVAR
#include <iostream>
#include <vector>
using namespace std;
void printNumbers(vector<int> numbers);
vector<int> addNumbers(vector<int> &numbers);
// #endif
- helper function numbers-functions.cpp
// numbers-functions.cpp
#include "numbers-header.hpp"
void printNumbers(vector<int> numbers){
if (numbers.size() == 0)
cout << "[] - the list is empty" << endl;
else {
cout << "[ ";
for (auto num: numbers)
cout << num << " ";
cout << "]" << endl;
}
}
vector<int> addNumbers(vector<int> &numbers){
int num_to_add {};
cout << "Enter an integer to add to the list: ";
cin >> num_to_add;
numbers.push_back(num_to_add);
cout << num_to_add << " added" << endl;
return numbers;
- include numbers.hpp in main file, but not numbers-functions.cpp! Compile and run main.cpp and numbers-functions.cpp, the linker will link the compiled binary files.
#include "numbers-header.h"
int main() {
printNumbers(numbers);
numbers = addNumbers(numbers);
}
safe way of variable initialisation
int catch_error{"abc"}
initialize local static variable (retains the value over multiple function calls (like a global variable, but on a local scope)
static int i{5000};
dynamic memory allocation and deallocation
size_t size{0};
double *temp_ptr {nullptr};
cout << "How much memory to allocate dynamically? ";
cin >> size;
temp_ptr = new double[size]; // allocate the storage on the heap
cout << temp_ptr << endl; // use it
delete [] temp_ptr; // release it
for loop by value:
for (const auto num: numbers) // &num to pass by reference
std::cout << num << std::endl;
}
for loop by reference:
for (const auto &num: numbers) // &num to pass by reference
std::cout << num << std::endl;
}
####Raw pointers
Always initialize them to avoid wild pointers!
int val{3};
int *ptr = &val;
// alternative with dynamic memory allocation
int *ptr = new int{3}
// don't forget to destroy dynamically allocated memory if not needed anymore!
delete ptr
####Smart pointers
Avoid new keywords!
#include <memory>
to use smart pointers
#####Unique Pointers
Use as default smart pointer.
They do not allow two unique pointers to point to the same memory. → No copying possible → move instead of assignment
E.g. initialization and move semantics:
std::unique_ptr<Test> t3;
t3 = std::move(t1);
Initialize with make_unique
auto song = std::make_unique<Test>("abba", "acdc");
E.g. unique pointer pointing to Base class, initialized with adress to a Derived class object (constructor arguments in parenthesis).
std::unique_ptr<Base> a1 = std::make_unique<Derived>("Moe", 5000);
Pass to function
auto int_ptr = std::make_unique<int>();
func(*int_ptr);
void func(int &i) return i++;
#####Shared Pointers Use if more complex memory management needed than unique pointers allow.
Several pointers pointing to the same object possible. They achieve it by introducing a pointer count. The pointer is deallocated only if no pointers are pointing to that memory anymore. → Can also be copied → More similar to raw pointes
ptr.use_count() can be used to determine how many shared pointers point to the object.
// std::shared_ptr<int> p1 {new int {100} }; Unefficient!
// More efficient initialization:
std::shared_ptr<Base> ptr = std::make_shared<Base>(100);
std::cout << "count:"<< ptr.use_count ();
ptr.reset(); // decrement the use_count; ptr is nulled out
#####Weak Pointers
- Few use cases (e.g. avoid cyclic reference that prohibits deletion --> solution: make one of the pointers a weak pointer)
- Always created from a shared pointer.
- Points to object on the heap.
- Does not participate in owning relationship → Doesnt change use_count()
Inheritence: models a "is a" relationship, e.g. student "is a" person. Composition: models a "has a" relationship, e.g. person "has an" account
Rule of thumb: Prefer modelling the system with composition over inheritence, because inheritence adds more complexity.
Use public inheritence to model is-a relationships --> most common Use private or protected inheritance to model has-a relationships --> not covered
Only use inheritence if it is appropriate!
import for std input/output
#include <iostream>
using namespace std;
std output
cout << "Enter your favorite number" << endl;
std input
std::cin >> favorite_number;
string declaration in cpp
#include <string> // add `using namespace std;` if necessary
string test = "Some text";
cout << test << endl;
To output objects of user-defined classes, use operator overloading for that class (in its .cpp). E.g.:
std::ostream &operator<<(std::ostream &os, const Account &account) {
os << "Account balance: " << account.balance;
return os;
}
create an 1d-array --> fixed size
giving the length explicitly
int numbers[4]{0, 1, 2, 3, 4}
giving the length implicitly
int numbers[]{0, 1, 2, 3, 4}
//
initialize all elements to 3.14
double arr[4] = {3.14};
create a vector (non fixed sized)
#include <vector>
using namespace std;
vector <int> examples(100, 98, 95, 93);
index the 5th vector element
examples.at(4) = 80;
adding value 80 at the end of vector
examples.push_back(80);
indexing last element of vector
examples.at(examples.size()-1) = 1000;
multidimensional vector
vector <vector<int>> movie_ratings
{
{1, 2, 3, 4},
{1, 2, 4, 4},
{1, 3, 4, 5}
};
Loop through all vector elements:
std::vector<int> numbers{1,2,3}
for (const auto num: numbers) // &num to pass by reference
std::cout << num << std::endl;
}