Full C++ Tutorial
h
programmeruser (418)

C++ Tutorial

So, this is a C++ tutorial. Thanks to @EpicGamer007 for the idea(s).
You should know some programming so I won't explain what a variable is and stuff like that.

Intro to C++

What is C++?

C++ is a general purpose programming language developed by Bjarne Stroustrap at Bell Labs as an extension to the C programming language (which was created at Bell Labs too by Dennis Ritchie as an extension to the B programming language, created by Ken Thompson).
(Photo of Bjarne Stroustrap as requested by @tsunami21)

Why should I use it?

C++ will teach you low-level concepts that will help you understand the computer. It's also extremely fast and there is a large developer ecosystem.

Here is a list of things/companies/organazations that use C++:

  • Google
  • Microsoft
  • Evernote
  • LinkedIn
    (source)
  • Game Development
  • Image Processing

  • Minecraft (Bedrock Edition)

  • Major browsers (Chrome, Firefox, Safari, etc.)
  • Windows
  • Adobe Systems

And languages:

Isn't C++ "dying"?

Well, no. Major companies still use it for the merits above and it's still being taught in colleges.

Compiling C++ Code

To compile C++ code with g++ do thiss:

g++ -o <program name> <program name>.cpp

Clang is just the same but with g++ replaced with clang++. I have no idea how Microsoft Visual C++ works so I can't help you with that.
After that you will want to run the binary:

./main

If you are on repl.it or an IDE then this will automatically be taken care of for you.

Hello, World! 👋

Let's start with a hello world program. I feel like a lot of tutorials don't focus enough on this program but it's filled with a lot of valuable things.

#include <iostream>

int main() {
  std::cout<<"Hello World\n";
  return 0;
}

You can find this code in the file helloworld.cpp in this repl.
So what is this code doing?

First we import the iostream library/header.
The "#" before include means that it is a preprocessor directive.
It basically tells the preprocessor to copy+paste the content of the file "iostream".

All execution starts in the main function (unless you specify otherwise in the compiler options)
Anything outside a function is one of the following:

  • A preprocessor directive
  • A variable declaration/definition
  • A function declaration
  • A using directive (will be gone over later)

One of the ways and the most common ways to declare functions/methods in C++ is in this format:

<return type> <function name>(<function arguments>) {
  <function body>
}

A function can return void which means nothing.
Usually the main function returns an integer as the exit status code.
You might see main functions like this:

int main(int argc, char* argv[]) {

}

where argc is the number of command line arguments and argv contains the arguments
if a function takes "void" as a argument like this:

int main(void) {

}

It just means that the function takes no arguments (but this is not nessecary in C++, only in C where not using void means that the function could take multiple or no arguments);

On the next line we see std::cout<<"Hello World\n";
What's the :: for?
It's the scope resolution operator, which is used to access things that are namespaced.
The "std" namespace contains c++ standard library functions and variables.
You might see some people putting "using namespace std" and then omitting "std::".
Don't do this! There can be name conflicts and it can make your code harder to read.
Instead if you want to omit "std::" only use "using" for one member such as "using std::cout".

There's an operator after that that looks like this: <<
You might ask: isn't this a bit shift operator? (it doesn't matter if you don't understand this)
Well C++ allows you to define what operators do for your classes (will be explained later)
Just remember for now that the << operator applied to a stream (don't worry if you don't know what this means, just know that std::cout is a stream) inserts strings and other stuff into the stream.
In this case we're inserting the string "Hello World\n".

Every statement in C++ ends with a semicolon (preprocessor directives and control structures don't).

Finally we return the exit status 0 (meaning the program was successful).

Printing multiple things at once

The << operator overload for the stream class allows chaining which means that you can apply this operator several times at once in succession to the class. So you could print multiple things at once like this:

#include <iostream>
int main() {
  std::cout<<"Hello "<<"World"<<"\n";
}

Comments

Single line comments start with a //, and multiline comments start with a /* and end with a */ like many other languages.

//Single line comment
/*
multiline comment

multiple lines
*/

Data Types and variables

C++ is a statically typed language unlike Python and like Java and C#. This means that variables can only have one type. There are primitive types which are prebuilt into C++ and there are non-primitive types which are defined by the programmer. There are also derived types which are derived from the primitive types such as arrays and pointers (source)

Here is a table of the primitive types in C++ from W3Schools:

Data TypeSizeDescription
int4 bytesStores whole numbers, without decimals
float4 bytesStores fractional numbers, containing one or more decimals. Sufficient for storing 7 decimal digits
double8 bytesStores fractional numbers, containing one or more decimals. Sufficient for storing 15 decimal digits
boolean1 byteStores true or false values
char1 byteStores a single character/letter/number, or ASCII values

Some non-primitive types but still useful are strings and vectors.

You can declare variables to hold these data types like this:

<type> <varname>;
<type> <varname> = <value>;

You don't have to give a variable a value on declaration but don't try to use these variables as it will cause what is called undefined behavior.

Now let's create some variables.

int main() {
  int my_int = 42;
  float my_float = 1.12; //it's recommended that you use doubles instead of floats
  double my_double = 1.12;
  bool my_bool = true; //bools are actually ints where 0 = false, 1 = true
  char my_char = 'a';
}

Strings? Where are the strings?

You might have noticed that I didn't define a string variable above. That's because there are multiple ways of defining them. First, there are C-strings:

//I'll go over this later
char my_char_array[] = "Hello World";
char* my_char_pointer = (char*) "Hello World";

Don't worry if you didn't understand that, I'll go over it later. The preferred method of defining strings in C++ is using the <string> header:

#include <string>
//remember that :: is the scope resolving operator, we're accessing the string type from the namespace std
std::string my_string = "Hello World"

The code for this part can be found in variables.cpp

Naming Conventions and Syntax

Operators

Here is a table with some of the most common C++ operators from W3Schools:

OperatorNameDescriptionExample
+AdditionAdds together two valuesx + y
-SubtractionSubtracts one value from anotherx - y
*MultiplicationMultiplies two valuesx * y
/DivisionDivides one value by anotherx / y
%ModulusReturns the division remainderx % y
++IncrementIncreases the value of a variable by 1++x
--DecrementDecreases the value of a variable by 1--x

Note about postfix operators: don't use them unless you need them. Using x++ increments the value of x but returns the value of the original, and has some performance implications. There's a joke that C++ increments C but returns the original value.

If / Else If / Else

This section is about if / else if / else statements.
Anything inside an if statement will run if the condition is true.
Anything in an else if statement will run if all of the previous conditions were false and the current one is true, and the code in the else statement will run if all of the previous conditions are false.
Syntax:

int i = 42;
if (i == 42) {
  std::cout<<"The number is 42\n";
} else if (i == 100) {
  std::cout<<"The number is 100\n";
} else {
  std::cout<<"The number is neither 42 nor 100.\n";
}

C++ supports the usual mathemetical comparision operators:

  • == - equals
  • < - less than
    • greater than
  • <= - less than or equals to
  • = - greater than or equals to
    Make sure not to mix up = and ==, = is used to assign to variables and == is used to compare two things.

C++ also supports logic operators:

  • && - and
  • || - or
  • ! - not

String Concatenation

If you want to concatenate C++ strings, just use a +:

std::string("Hello ") + std::string("World")

Make sure you create a new instance of the std::string class or it will not work (since the default string type doesn't support it)

Loops

Loops are control structures that repeat code a specified number of times.
There are three types of loops in C++ and I will be going over every one of them except for the range based for loop.

A for loop is a control structure for specifying iteration (source).
There are two types of for loops: the traditional type and the range based for loop (which I will not be going over here).
The basic structure of a traditional for loop in C++ is:

for (start_expr; iter_expr; expr) {

}

where start_expr is executed on start, iter_expr is evaluated every iteration and the loop continues only if the statement is true. expr is evaluated every iteration. (source).
Here's an example that prints the numbers from 1 to 10:

for(int i = 1; i <= 10; ++i) {
  std::cout<<i<<"\n";
}

We start at 1, continue if i is less than or equal to 10, and increment i every time.

While loops run while a condition is true.

int i = 1;
while(i <= 10) {
  std::cout<<i<<"\n";
  ++i;
}

Make sure the loop can end (unless it's supposed to be something like a game loop).

A do..while loop is just a while loop but the condition is evaluated at the end instead of the beginning (which means that the body is executed at least once):

//prints 1,2,3,4,5,6,7,8,9,10,11
int i = 1;
do {
  std::cout<<i<"\n";
  ++i;
} while(i <= 10)

There are also the keywords break and continue. Break means exit out of the loop:

//prints 1and 2 on seperate lines instead of 1,2,3,4,5,6,7,8,9,10
int i = 1; 
while (i <= 10) {
  if(i == 3) {
    break;
  }
  std::cout<<i<<"\n";
}

And continue means to skip the rest of the current iteration:

//doesn't print 3
int i = 1;
while(i <= 10) {
  if(i == 3) {
    continue;
  }
  std::cout<<i<<"\n";
}

These work for the for loop too.

Switch

Switch is just if / else if / else but cleaner. It only works for numbers (not strings).
make sure to put break (if you have a default case) so that the default case won't run.

int my_num = 3;
switch(my_num) {
  case 1:
    std::cout<<"my_num is 1\n";
    break;
  case 2:
    std::cout<<"my_num is 2\n";
    break;
  case 3:
    std::cout<<"my_num is 1\n";
    break;
  case 4:
    std::cout<<"my_num is 1\n";
    break;
  case 5:
    std::cout<<"my_num is 1\n";
    break;
  default:
    std::cout<<"my_num is not 1,2,3,4, or 5.\n";
}

Arrays

To declare an array in C++ use this syntax:

<type> <name>[size];
<type> <name>[size] = {<items>};
<type> <name>[size] = {<items>};

Note that declaring an array with the third method will automatically make the length the length of the initializer list.

To access an item in an array, use this:

array_name[index]

where index is the index of the item. This actually means something else that we'll go over later.
There is an alternative way of defining a string with a char array:

char my_char_array[] = "Hello World";

Functions

Functions are pieces of code that can be called several times. We already went over how to declare these before. More information:

  • A function that returns a type must return that type
  • You define your arguments just like variables

Function Prototypes

To use a function before you define it you can use what are called function prototypes which look like this:

int add(int a, int b);

Function prototypes specify the name of the function and the arguments. The compiler then looks for the definition in the file. Function prototypes must end with a ;. They're basically just function definitions stripped of their body.

Function Overloading

You can accept different arguments for the same function. Let's say we want to get the name of a dog ("borrowed" from @EpicGamer007's tutorial)
What if we want to return dog#xxxxx if an ID is passed and the same string if a string is passed? We can do this with function overloading. C++ allows uus to accept different arguments for the same function and to do different things (as long as the arguments are different)):

#include <string>
std::string dogname(int id) {
  //std::to_string converts int to string
  return std::string("dog#") + std::to_string(id);
}
std::string dogname(std::string name) {
  return name;
}

Templates

Templates are a feature of C++ that allows you to work with generic types. Let's say for example (this is really useless but it's a teaching example) you're creating a function to return the array that was passed as an argument. This is pretty easy, but how can we accept arrays of any type? We use what are called templates, which allow us to specify a class when calling the function and our function can use that generic type. We can define a template like this:

template <typename T>
T return_array(T arr[]) {
  return arr;
}

Notice that we can use T in place of any where where a type is suppposed to be since a typename is passed as an argument to the template.

Make sure that the template is right above the function.

Now we can call the function like this with any type:

std::string my_arr[] = {"Hello","World"};
int my_int_arr = {1,2,3};
return_array<std::string>(my_arr);
return_array<int>(my_int_arr);

Note how we pass the typename as a argument to the template.

Pointers and References

Pointers

Pointers are data types that hold the meory location of a value. You can create a pointer like this:

<type>* <pointer name> = <value or memory location>

To get the memory location of a variable put a & before it:

&varname

And to get the value that the pointer points to:

*varname

which is called dereferencing a pointer.
There's yet another way to declare (C)
strings in C++:

char* my_string = (char*) "Hello World";

which is declaring a pointer to a string. The strange (char*) will be explained later means that you are typecasting, or casting the type to another. In C++ you can't just change a string literal to (char*). There's a link at the bottom that explains this.

Things NOT to do with pointers

  • Don't dereference a null pointer
    If you try to dereference a NULL pointer (the default value for pointers) then a memory error will occur.
  • Don't dereference a pointer and set that
    If you try to do this:
char* my_string = (char*) "Hello World";
*my_string = "changed";

a segfault (memory error) will occur. This is because you're essentially trying to do this:

"Hello World" = "changed";

You can't change the value of a value! Instead just change the variable directly:

my_string = "changed";

References

References are C++'s way of passing references. References are more memory safe than pointers. They're mainly used for passing references to functions. To create a reference use the below syntax:

<type>& <varname>

So int& x would be a variable that is a reference to another variable. Let's pass a reference to a function to swap two cariables:

void swap(int& a, int& b) {
  int temporary_var = b;
  b = a;
  a = temporary_var;
}

Notice that we can modify the variable since we passed a reference to the variable not the value.

OOP

(@EpicGamer007 gave me a lot of ideas for this part.)
OOP, or object oriented programming is when "objects interact with objects" (quoting EpicGamer007).
The simplest class declaration is just class MyClass{};. Class declarations have a semicolon at the end of them unlink if statements, loops, and function declarations. How can we construct an instance now? The standard syntax is

MyClass instance;
MyClass instance(<args>);
MyClass(<args>)
MyClass instance = MyClass(<args>)

If the constructor doesn't take any arguments you can omit the parentheses. The third method is if you want to create an instance as a value.
Now let's create a Dog class:

#include <string>
class Dog {
  public:
    int age;
    std::string name;
}

Notice how we declared the class properties like regular variables. We had to put the public: label since all data in C++ is private by default (explained in the Access Modifiers section).
Now we can create an instance:

Dog my_dog;

and we can assign properties to it with dot notation:

my_dog.age = 5;
my_dog.name = "Matthew";

Class Methods

We can create methods just like functions. Let's make a method for the dog to bark:

void bark() {
  std::cout<<name<<" barks!\n";
}

When accessing class members just use the name of the member.
We can call the method just like we can access members:

my_dog.bark();
//output: Matthew barks!

Access Modifiers

Now let's say someone really malicious uses our Dog class:

Dog my_dog;
my_dog.age = -1000000000000000000;
my_dog.name ="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

Ages can't be negative and that string doesn't look good. Now let's say this is something like a game or banking system. We don't want people to be able to randomly set our data. We can fix this with access modifiers:

class Dog {
  private:
    int age;
    std::string name
}

The private label tells C++ that only functions inside the same class can access and modify the data, no one else can. We can also do this with methods. But how can things outside the class access the data?

Getters and Setters

Getters and setters are methods that allow things outside the class to access and modify the data.

We can create some methods now to do that:

int get_age() {
  return age;
}
void set_age(int age_arg) {
  if(age_arg > -1) {
    age = age_arg;
  }
}
std::string get_name() {
  return name;
}

Now we can set and get data like this:

Dog my_dog;
my_dog.setAge(5);
my_dog.setName("Matthew");
std::cout<<"The dog's name is "<<my_dog.get_name()<<" and his age is "<<my_dog.get_age()<<"\n";
my_dog.bark();

And we can do checks to make sure the getter/setter is authorized (assume there's a Person class):

private:
  int social_security_number;
public:
  int get_social_security_number(Person person) {
    if(person.status == "GovernmentWorker" || person.status == "Owner") {
      return social_security_number;
    }
  }

Constructors

Constructors are pieces of code that are called whenever an instance is created. They can take arguments to initialize the class. They're created like this:

<name_of_your_class>(<args>) {

}

There are also destructors but that's another topic for dynamic memory. Now let's create a constructor for our dog class:

Dog(int init_age, std::string init_name) {
  name = init_nname;
  if(init_age > -1) {
    age = init_age;
  }
}

And we can change our code to create the dog class like this:

Dog my_dog(5,"Matthew");

Constructors can be overloaded just like functions/methods (just assume there are the functions dog_name_from_ssn and dog_age_from_ssn):

Dog(int init_age, std::string init_name) {
  name = init_nname;
  if(init_age > -1) {
    age = init_age;
  }
}
Dog(int ssn) {
  name = dog_name_from_ssn(ssn);
  age - dog_age_from_ssn(ssn);
}

And constructors can also use templates.

Operator Overloading

Operator overloading is defining what your class will do when someone uses an operator on it like +. For example std::string concatenates the two strings. You can define a operator overload like this

<return> operator <operator> (<argument>) {

}

Let's declare a number class that stores an integer (this is really useless, it's just for learning purposes):

class Number {
  private:
    int value;
  public:
    Number(int init_value) {
      value = init_value;
    }
    int get_value() {
      return value;
    }
    //The second argument is 
    //a. The other instance of the class when the operator is a binop
    //b. whatever argument it is for something like square brackets []
    Number operator + (Number other) {
      return Number(value + other.get_value());
    }
}

Now we can add the two classes together:

#include <iostream>

Number num1(5);
Number num2(10)
Number result = num1 + num2;
std::cout<<result.get_value()<<"\n";

Inheritance

Inheritance is when a class inherits (basiclly deriving) from a base class. This is useful when we want to extend a class, such as extending the Animal class as a Penguin. Let's do that now:

class Animal {
  protected:
    std::string name;
  public:
    Animal(std::string init_name) {
      name = init_name;
    }
    animal_sound() {
      std::cout<<name<<" makes a [animal sound]";
    }
};
//notice how we use : then public Animal
//The access specifier specifies WHAT access we want to give the inherited methods and members
class Penguin: public Animal {
  //notice how we call the parent constructor by adding a : between the constructors to call the base class constructor
  Penguin(std::string name): Animal(name) {
    //do something here
  }
  virtual animal_sound() {
    std::cout<<name<<" makes a penguin sound";
  }
};

//creating an instance
Penguin penguin("Tux");

The protected access modifier

The public modifier specifies that things outside the class cannot access those members nor derived classes, and the protected modifier spcecifies that derived classes and the class itself can only access it. This way the data isn't public but derived classes can still use it.

The virtual keyword

Let's say the base class and derived class have an indentical function. When creating a instance of the derived class, which method should C++ call when the method that is present in both classes is called? The virtual keyword FORCES C++ to overide the base class method.

More about OOP

This should cover the basics of OOP, if you want more read about it see multilevel inheritance, multiple inheritance, polymorphism, and static.

Standard library

Input

For input, just use the getline method of iostream:

std::string in;
std::getline(std::cin,in);

std::getline is a stream function that exracts one line from it.

Lists

Arrays in C++ can't change their size. We can solve that with the std::vector type from the header <vector>:

#include <vector>
std::vector<int> my_list = {1,2};
my_list.push_back(3);
my_list.push_back(4);
my_list.push_back(5);

Here's a list of basic methods:

  • vector.push_back - push a item to the end of the vector
  • vector.pop_back - pop an array from the vector

File I/O

To interact with files #include the header <fstream>. It provides a few options for working with files:

  • ifstream - read only from a file
  • ofstream - write only to a file
  • fstream - read and write to a file
    To read and write to a file just do it just as you would for std::cout and std::cin (they're all streams):
//includes
#include <fstream>
#include <string>
#include <iostream>
//create an instance of std::fstream
std::fstream file("myfile.txt");
//insert data
file << "Hello World!";
std::string line;
//while there are still lines to read print them out
while(std::getline(file,line)) {
  std::cout<<line<<"\n";
}

More about headers

When writting C++ libraries, people usually have a header and source files. Why though? It's because you want to seperate implemetation from the interface. Header files allow you to see the functions the libraries provide without digging into the source code, and allow you to link with libraries instead of compiling with them. So what do these header files look like?

class MyClass {
  public:
    MyClass(int my_arg);
    int my_member;
}
void my_func();

They just contain function prototypes, variable declarations and class declarations. Now what would a source file look like?

#include "my_header.hpp"
MyClass::MyClass(int my_arg) {
  //do something
} 
void my_func() {
  //do something
}

And in the main file:

#include "my_header.hpp"

And to compile:

g++ -o main main.cpp my_source.cpp

Extra things:

en.cppreference.com is a great reference resource for C++
cplusplus.com forums
Typecasting allows you to change the type of a value. More here
What square brackets actually mean
Learn about C++ lambdas here

You are viewing a single comment. View All
EpicGamer007 (1251)

@programmeruser wait what. i thought u said my tutorial was bad