Progress
I keep short checkpoints here, mostly to remember what each lesson gave me and where I should return later.
0.1#
- LearnCpp teaches from the basics to modern C++.
- Do not memorize everything. Return to lessons and documentation.
- You need to write code, not only read it.
0.2#
- A program is a sequence of instructions executed by a computer.
- Programs run on a platform, meaning a combination of hardware and software.
- Portable code is code that is easy to run on different platforms.
0.3#
- C++ started as an extension of C, but it is not just C with add-ons.
- C++ gives a lot of control, but you need to watch out for pitfalls.
- C++ works well where performance and resource control matter.
0.4#
- Programming starts with defining the problem.
- Then you plan the solution, and only after that you write code.
- The main source file is usually named
main.cpp.
0.5#
- The compiler translates
.cppfiles into object files. - The linker combines object files and libraries into an executable.
- Build is the whole process of creating an executable program from source code.
0.6#
- An IDE combines an editor, compiler, linker, build system, and debugger.
- For C++, use a compiler that supports at least C++17, preferably newer.
- VS Code works, but it requires better setup than a full IDE.
0.7#
- A project is a container for program files and build settings.
- A console project is enough for learning C++.
- Build compiles and links the program, and run launches the finished executable.
0.8#
- If
cin,cout, orendlare undefined, check#include <iostream>andstd::. - A C++ program must have exactly one
main()function. - If the program compiles but behaves incorrectly, you need to debug it.
0.9#
- A debug build has debugger information and usually no optimization.
- A release build is optimized and has no extra debug data.
- Use
-ggdbfor debug, and-O2 -DNDEBUGfor release.
0.10#
- Compiler extensions are non-standard compiler additions.
- They can make code work on your machine even though it is not standard C++.
- Disable extensions with
-pedantic-errors.
0.11#
- Compiler warnings help find potential bugs.
- It is worth enabling more warnings:
-Wall -Wextra -Wconversion -Wsign-conversion. -Werrortreats warnings as errors.
0.12#
- Set the language standard with a flag, e.g.
-std=c++23. - Without this flag, the compiler may use its default standard.
- Set the standard explicitly so you know which C++ version you are using.
0.13#
- The
__cplusplusmacro lets you check which standard the compiler is using. 202002Lmeans C++20, and202302Lmeans C++23.- If you get C++20 without
-std=c++23, that means C++20 is your compiler's default standard.
1.1#
- A statement is an instruction that performs some action.
- A function is a group of statements executed sequentially.
- A C++ program starts from the
main()function.
1.2#
- Comments describe code for humans.
//creates a single-line comment./* */creates a block comment, but do not nest block comments.
1.3#
- Data is information processed, stored, or transmitted by a computer.
- A value is a single piece of data, e.g.
5,'H', or"Hello". - An object is a region of memory that stores a value.
- A variable is a named object.
- A type determines how to interpret the value stored in an object.
1.4#
assignmentmeans giving a value to a variable that already exists.initializationmeans giving an initial value when creating a variable.=means assignment, and==means comparison.- Prefer brace initialization, e.g.
int x { 5 };. int x {};is value-initialization. For a localint, it gives the value0.- Avoid uninitialized variables, e.g.
int x;. - Braces
{}block narrowing conversions, e.g.int x { 4.5 };gives an error. int x = 4.5;orint x(4.5);may truncate the value to4.- If the value will be overwritten soon, use e.g.
int x {}; std::cin >> x;. - Do not define multiple variables on one line.
int a, b = 5;means onlybhas the value5.[[maybe_unused]] int x { 5 };means the variable may be intentionally unused.
1.5#
#include <iostream>includes theiostreamheader, which gives usstd::coutandstd::cin.std::coutprints data to the console.std::cinreads data from the keyboard.- For a normal newline, prefer
'\n'or"\n", notstd::endl. std::endldoes newline + flush, so it can be slower.std::cinandstd::coutuse buffers.std::cin >> xextracts as much data as fits the type ofx.- Data that
std::cindoes not extract remains in the buffer. - When reading into a variable, still initialize it, e.g.
int x {};.
1.6#
- C++ usually does not automatically initialize local variables of simple types, e.g.
int x;. - An
uninitialized variableis a variable that has not yet received a known value. initializedmeans a value given at definition, whileassignmentmeans a value given later.int x;is default-initialization, but for a localint, it usually leaves an indeterminate value.- Using an uninitialized variable may print a garbage value.
- Using the value of an uninitialized variable causes undefined behavior.
undefined behaviormeans the C++ standard does not define what should happen.- Code with UB may appear to work, crash, produce random results, or change behavior after a small code change.
- Always initialize variables, e.g.
int x {};orint x { 5 };. implementation-defined behavioris behavior chosen and documented by a given implementation.unspecified behavioris implementation-dependent behavior without the requirement to document the choice.
1.7#
keywordsare reserved C++ words, e.g.int,return,if; you cannot use them as your own names.- An
identifieris a name given to something in code, e.g. a variable, function, or type. - An identifier can contain letters, digits, and
_, but it cannot start with a digit. - C++ is case-sensitive, so
value,Value, andVALUEare different names. - Variables and functions usually start with a lowercase letter.
- For multi-word names, consistently use either
camelCaseorsnake_case. - In an existing project, match the style of that project.
- Do not start your own names with
_, because such names may be reserved. - A name should say what the value means, e.g.
customerCountinstead ofccount. - Avoid unclear abbreviations, because code is read more often than it is written.
1.8#
whitespacemeans spaces, tabs, and newlines used to separate language elements and format code.- Some elements must be separated by whitespace, e.g.
int x;, becauseintxwould be a different identifier. - Whitespace inside text is literal, e.g.
"Hello world!"and"Hello world!"are different strings. - Adjacent strings separated only by whitespace are concatenated, e.g.
"Hello " "world!". - C++ is whitespace-independent, so formatting usually does not change meaning, but it affects readability.
- Code inside braces should be indented one level.
- LearnCpp uses a style where the opening function brace is on a separate line.
- Long lines should be split, usually around 80 characters.
- When breaking a long expression, put the operator at the beginning of the next line.
- In an existing project, match the style of that project.
- Use automatic formatting, e.g. an editor formatter or
clang-format.
1.9#
- A
literalis a fixed value written directly in code, e.g.5,"Hello", or'\n'. - A literal has a value and a type, but its value is fixed and cannot be changed.
- A variable represents a place in memory whose value can be read and changed.
- An
operatorperforms an operation on operands, e.g.3 + 4. - An
operandis a value an operator works on, e.g. in3 + 4, the operands are3and4. - The result of an operation in C++ is often called the
return value. - The same operator can have different meanings depending on context, e.g.
-5and4 - 3. - Operators can be combined, and the result of one operation can become the operand of another.
- A
side effectis an observable effect besides returning a value, e.g.x = 5changesx, andstd::cout << 5prints to the console. operator=andoperator<<return the left operand, which allows chaining, e.g.x = y = 5orstd::cout << "Hello " << "world!".
1.10#
- An
expressionis a piece of code that evaluates to a result, e.g.2 + 3,x,five(). evaluationis the process of computing an expression, and theresultis the output of that expression.- Where C++ expects a single value, you can use an expression, e.g.
int x { 2 + 3 };. - An expression does not end with a semicolon. The semicolon ends the statement containing the expression.
- An
expression statementis an expression followed by a semicolon, e.g.x = 5;. - In an
expression statement, the expression result is discarded, so expressions with side effects are usually the meaningful ones. 2 * 3;is syntactically valid but useless, because the result6is discarded.- A
subexpressionis an expression that is part of a larger expression, e.g.4 + 5inx = 4 + 5. - A
compound expressionis an expression with at least two operators, e.g.x = 4 + 5.
1.11#
- This lesson practices building a simple program with
std::cin,std::cout, variables, and expressions. - A program can be split into input, calculation, and output.
- If input will be used later, do not overwrite it unnecessarily.
- If a result is used only once, you can calculate it directly where it is used, e.g.
std::cout << num * 2;.
1.x#
- Chapter 1 collects the basics: statement, function, variable, type, initialization, input/output, UB, operators, expressions.
- The most important pitfalls in the chapter: uninitialized variables,
std::endl, narrowing conversion, and thestd::cinbuffer. - Coding task from the quiz: a program that reads two numbers and prints addition and subtraction.
2.1#
- A function is a reusable sequence of statements that performs a specific task.
- A
function callpauses the current execution point, runs the called function, and returns after it finishes. - The
calleris the function that calls, and thecalleeis the function being called. - A
function headercontains the return type, name, and parameters of the function. - A
function bodyis the code between{}. - Functions can be called many times.
- Functions can call other functions.
- C++ does not allow defining functions inside other functions.
2.2#
- A function with a non-
voidtype returns a value to the caller. - The type before the function name is the
return type. return expression;evaluates the expression, returns its result to the caller, and ends the function.- Returning a value works as return by value.
- The caller can use the returned value or ignore it.
- A function with a non-
voidreturn type must return a value on all execution paths. - Missing
returnin a value-returning function causes undefined behavior. main()must returnintand should not be called manually.return 0;frommain()means normal program termination.- A function can return only one value per call.
- DRY means
Don't Repeat Yourself.
2.3#
voidas a return type means the function does not return a value.- A
voidfunction can perform actions, e.g. print text or modify data. - A
voidfunction automatically returns to the caller after reaching the end of its body. - Do not put
return;at the end of avoidfunction, because it is unnecessary. return;without a value can make sense in avoidfunction if you want to exit early.- A
voidfunction cannot be used where an expression producing a value is required. - Returning a value from a
voidfunction is a compile error.
2.4#
- A
parameteris a variable in the function header. - An
argumentis the value passed in a function call. - The number of arguments usually must match the number of parameters.
- With ordinary parameters, the argument value is copied into the parameter. This is
pass by value. - Parameters that use
pass by valuearevalue parameters. - An argument can be any valid expression.
- A function return value can be passed directly as an argument to another function.
- Parameters and return values together let you write functions like: take data, calculate something, return the result.
- An
unreferenced parameteris a parameter that exists but is not used in the function body. - If a parameter must exist but is unused, you can omit its name.
2.5#
- A
local variableis a variable defined inside a function. - Function parameters are also treated as local variables.
lifetimeis the time an object exists: from creation to destruction. This is runtime.- Local variables are destroyed at the end of the block
{}in reverse order of creation. scopeis the region of code where an identifier is visible. This is compile-time.- A local variable from one function is not visible in another function.
- Functions can have local variables with the same names, and these are separate objects.
- With
pass by value, a function parameter is a separate copy of the argument. - Changing a parameter passed by value does not change the variable in the caller.
- Define local variables as close as possible to their first use.
- Use a parameter when the caller should provide the initial value.
- Use a local variable when the value should be created inside the function.
- A
temporary objectis an unnamed object created by the compiler for a short time. - Temporary objects are destroyed at the end of the full expression.
2.6#
- Functions help organize code, remove repetition, test, extend a program, and hide implementation details.
- Code repeated more than once is a good candidate for a function.
- Code with clear input and output is also a good candidate for a function.
- A function should do one specific thing.
- Do not combine calculating a result and printing a result in one function if they can be separated cleanly.
- When a function becomes long or hard to understand, you can split it into smaller functions. This is refactoring.
- A simple program can often be split into input, calculation, and output.
2.7#
- The compiler reads a file from top to bottom.
- If you call a function before the compiler has seen it, you get an error.
- A
forward declarationannounces a function before its definition. - For functions, a forward declaration is written as a function prototype.
- A function prototype contains the return type, name, parameters, and semicolon, but no function body.
- Parameter names in a declaration are optional, but writing them helps readability.
- If a function is declared and called, but has no definition anywhere, compilation can pass, but the linker fails.
- A
declarationtells the compiler that an identifier exists and what type it has. - A
definitionimplements a function or creates a variable. - Every definition is a declaration, but not every declaration is a definition.
- A
pure declarationis a declaration without a definition. - ODR means you cannot define the same thing twice in the same scope.
- Forward declarations are especially important with multiple
.cppfiles.
2.8#
- Larger programs are split into multiple
.cppfiles. - When compiling from the terminal, you must pass all
.cppfiles. - The compiler compiles each file separately and does not remember the contents of other files.
- If
main.cppuses a function frominput.cpp,main.cppmust know its declaration. - A forward declaration satisfies the compiler, and the linker later connects the call to the definition in another file.
- Missing a forward declaration gives a compile error.
- Missing a definition or missing a
.cppfile in compilation gives a linker error. - Each
.cppfile that usesstd::coutorstd::cinmust have its own#include <iostream>. - Do not include
.cppfiles. Compile them together.
2.9#
- A
naming collisionis a name conflict where two identifiers have the same name and the compiler or linker cannot distinguish them. - A name conflict in the same file usually gives a compile error, while a conflict between
.cppfiles usually gives a linker error. - A
scope regionis a region of code where names must be unique. - A
namespacecreates a separate scope for names and helps avoid conflicts. - Things not defined in a function, class, or namespace go into the
global namespace. - The C++ standard library is in the
stdnamespace, so we write e.g.std::cout. ::is the scope resolution operator, e.g.std::coutmeanscoutfrom thestdnamespace.- A name with a namespace prefix, e.g.
std::cout, is aqualified name. - Prefer explicit namespace prefixes, e.g.
std::cout, instead ofusing namespace std;. - Avoid
using namespace std;, especially at the top of a file and in headers, because it increases the risk of naming conflicts.
2.10#
- Before compilation, each
.cppfile goes through preprocessing. - The
preprocessormakes textual changes to code, but it does not modify the original files. - The result of preprocessing is a
translation unit, meaning a.cppfile after directives such as#includehave been expanded. - A
preprocessor directivestarts with#and ends at the newline, not with a semicolon. #includeinserts the contents of the specified file in place of the directive.#definecreates a macro, meaning a rule for textual replacement.- Avoid macros with replacement text, e.g.
#define PI 3.14, if there is a normal C++ alternative. - Macros without replacement text can be used as switches for conditional compilation.
#ifdef,#ifndef, and#endiflet you conditionally include or exclude code from compilation.#if 0 ... #endiflets you temporarily exclude a block of code from compilation.#defineworks from the place of definition to the end of the current file, unless it reaches another file through#include.
2.11#
- A
header fileis a header, usually.h, used mainly for declarations. - A header lets you keep declarations in one place and include them where they are needed.
- Include your own headers with
" ", e.g.#include "add.h". - Include standard headers with
< >, e.g.#include <iostream>. - A header paired with a
.cppfile should have the same base name, e.g.add.handadd.cpp. - The
.cppfile should include its paired header, e.g.add.cppshould have#include "add.h". - For now, put declarations in headers and keep function definitions in
.cppfiles. - Function definitions in a header can violate ODR if the header is included into multiple
.cppfiles. - Do not include
.cppfiles. Add them to compilation. - Each file should include the headers it needs directly, without relying on transitive includes.
- A header that uses things from another header should include that header itself.
- Every header should have a header guard.
2.12#
- A
header guardprotects a header from being included multiple times into the sametranslation unit. - Every custom header should have a header guard.
- A classic header guard uses
#ifndef,#define, and#endif. - The guard name should be unique, usually based on the file name, e.g.
ADD_Hforadd.h. - The header guard allows the header contents through on the first
#include, and skips later includes in the same file. - A header guard does not block including the same header in different
.cppfiles. - A header guard does not replace the rule: declarations in
.h, function definitions in.cpp. #pragma oncedoes something similar and is convenient, but it is not part of the C++ standard.
2.13#
- Before writing code, first define the goal of the program.
- Then define the requirements, meaning what the program should do and what limits it has.
- Break a hard problem into smaller steps.
- Splitting a problem into steps naturally leads to splitting a program into functions.
- First set the order of program actions, e.g.
input -> calculation -> output. - Implement the program in small stages: write a piece, compile, test.
- Do not write the whole program at once.
- First make a simple working version, then expand it.
- Do not polish the first code version too early.
- At the beginning, readability and maintainability matter more than micro-optimization.