cpp

Contents

  1. 1.0 - C++ Basics
    1. 1.1 - Statements
    2. 1.2 - Comments
    3. 1.3 - Objects and Variables
    4. 1.4 - Variable Assignment and Initialization
      1. 1.4.1 - Assignment
      2. 1.4.2 - Initialization
        1. 1.4.2.1 - Default Initialization
        2. 1.4.2.2 - Copy Initialization
        3. 1.4.2.3 - Direct-Initialization
        4. 1.4.2.4 - List-Initialization
        5. 1.4.2.5 - Value-Initialization
      3. 1.4.3 - [[maybe_unused]]
    5. 1.5 - iostream: cout, cin, endl
      1. 1.5.1 - std::cout
      2. 1.5.2 - std::endl
      3. 1.5.3 - std::cin
    6. 1.7 - Keywords and Naming Variables
    7. 1.9 - Introduction to literals and operators

1.0 - C++ Basics

1.1 - Statements

Types of statements:

  1. declaration statements
  2. jump statements
  3. expression statements
  4. compound statements
  5. selection statements (conditionals)
  6. iteration statements (loops)
  7. try blocks

NOTE

Computers have “control characters” that have a meaning but aren’t meant to displayed traditionally. e.g. “escape”, “tab”, “backspace”

1.2 - Comments

Use comments for

  1. describing any program, library, or function (intention)
  2. within the program/library/function describe how the code is going to accomplish its goal (method)
  3. explain certain statements, reasonings
// The player just drank a potion of blindness and can not see anything
sight = 0;

TIP

JavaDoc is a way of writing api specifications (function descriptions) in a standard way to help generate documentation easier. Tools like Doxygen help generate documentation

1.3 - Objects and Variables

Variables: single piece of data. objects with names Literals: Values placed directly in source code Objects: regions of storage that can hold a value

TIP

avoid defining multiple variables in a single statement

int a; double b; // correct (but not recommended)
 
// correct and recommended (easier to read)
int a;
double b;

1.4 - Variable Assignment and Initialization

1.4.1 - Assignment

= is the assignment operator

int width; // define an integer variable named width
width = 5; // assignment of value 5 into variable width

Copy-assignment: By default assignment copies the value of the RHS to the variable on the LHS.

1.4.2 - Initialization

Assignment takes two statements for a variable to have a value

  • defining
  • assigning

Combining the two gives you Initialization

#include <iostream>
 
int main()
{
    int width { 5 };    // define variable width and initialize with initial value 5
    std::cout << width; // prints 5
 
    return 0;
}

There are 5 common types of initialization

int a;         // default-initialization (no initializer)
 
// Traditional initialization forms:
int b = 5;     // copy-initialization (initial value after equals sign)
int c ( 6 );   // direct-initialization (initial value in parenthesis)
 
// Modern initialization forms (preferred):
int d { 7 };   // direct-list-initialization (initial value in braces)
int e {};      // value-initialization (empty braces)

NOTE

Other forms include

  1. Aggregate Initialization 13
  2. Copy-List-Initialization Below
  3. Reference Initialization 12
  4. Static, constant, and dynamic-Initialization 7
  5. Zero-initialization Below

1.4.2.1 - Default Initialization

When no initializer is provided, leaves the variable with indeterminate value (garbage)

1.4.2.2 - Copy Initialization

Inherited from C

int width = 5; 

Copy-initialization had fallen out of favor in modern C++ due to being less efficient than other forms of initialization for some complex types. However, C++17 remedied the bulk of these issues, and copy-initialization is now finding new advocates

NOTE

Copy-initialization is also used wherever values are implicitly copied,

  • passing arguments to function by value
  • returning from function by value
  • catching exceptions by value

1.4.2.3 - Direct-Initialization

int width (5);

Direct-initialization was initially introduced to allow for more efficient initialization of complex objects (those with class types, which we’ll cover in a future chapter). Just like copy-initialization, direct-initialization had fallen out of favor in modern C++, largely due to being superseded by direct-list-initialization. However, direct-list-initialization has a few quirks of its own, and so direct-initialization is once again finding use in certain cases.

TIP

Direct-initialization is also used when values are explicitly cast to another type (e.g. via static_cast)

1.4.2.4 - List-Initialization

int width { 5 };    // direct-list-initialization of initial value 5 into variable width (preferred)
int height = { 6 }; // copy-list-initialization of initial value 6 into variable height (rarely used)

List-initialization was introduced to provide a initialization syntax that works in almost all cases, behaves consistently, and has an unambiguous syntax that makes it easy to tell where we’re initializing an object.

IMPORTANT

List-Initialization disallows narrowing conversions What would normally be a warning becomes an error see the below snippet

int main()
{
    // An integer can only hold non-fractional values.
    // Initializing an int with fractional value 4.5 requires the compiler to convert 4.5 to a value an int can hold.
    // Such a conversion is a narrowing conversion, since the fractional part of the value will be lost.
 
    int w1 { 4.5 }; // compile error: list-init does not allow narrowing conversion
 
    int w2 = 4.5;   // compiles: w2 copy-initialized to value 4
    int w3 (4.5);   // compiles: w3 direct-initialized to value 4
 
    return 0;
}

1.4.2.5 - Value-Initialization

When a variable is initialized using an empty set of braces, a special form of list-initialization called value-initialization takes place. In most cases, value-initialization will implicitly initialize the variable to zero (or whatever value is closest to zero for a given type). In cases where zeroing occurs, this is called zero-initialization.

int width {}; // value-initialization / zero-initialization to value 0

NOTE

For class types, value-initialization (and default-initialization) may instead initialize the object to predefined default values, which may be non-zero

1.4.3 - [[maybe_unused]]

C++17 introduced the [[maybe_unused]] attribute, which allows us to tell the compiler that we’re okay with a variable being unused. The compiler will not generate unused variable warnings for such variables.

The following program should generate no warnings/errors:

#include <iostream>
 
int main()
{
    [[maybe_unused]] double pi { 3.14159 };  // Don't complain if pi is unused
    [[maybe_unused]] double gravity { 9.8 }; // Don't complain if gravity is unused
    [[maybe_unused]] double phi { 1.61803 }; // Don't complain if phi is unused
 
    std::cout << pi << '\n';
    std::cout << phi << '\n';
 
    // The compiler will not warn about gravity not being used
 
    return 0;
}

The [[maybe_unused]] attribute should only be applied selectively to variables that have a specific and legitimate reason for being unused (e.g. because you need a list of named values, but which specific values are actually used in a given program may vary). Otherwise, unused variables should be removed from the program.

There are more such attributes here

  • [[nodiscard]] – when specified with a function declaration, tells the compiler to emit a warning if function’s return value is ignored; when specified with a struct emits a warning wherever the struct is returned and ignored.
  • [[fallthrough]] – suppresses compiler warning about a switch-case statement without a break; in other words, a case statement that falls through into another case.
  • [[no_unique_address]] – tells the compiler to perform empty base optimization on marked data member.
  • [[deprecated]] – emits a warning when marked function, struct, namespace, or variable is used.
  • [[noreturn]] – tells the compiler that the marked function never returns; emits a warning if it does.
  • [[maybe_unused]] – suppressed a warning when marked variable, function, or argument is not used.
  • [[likely]] – tell the compiler this is the most likely path of execution; allows for optimizations.

1.5 - iostream: cout, cin, endl

1.5.1 - std::cout

is buffered i.e. lines your text onto the train station, and eventually the train will take it to the console

Key Insight

Writing data to a buffer is typically fast, whereas transferring a batch of data to an output device is comparatively slow. Buffering can significantly increase performance by batching multiple output requests together to minimize the number of times output has to be sent to the output device.

1.5.2 - std::endl

does two things

  • moves cursor back to beginning (new line)
  • flushes the buffer (train leaves the station) buffer flushing is slow, so prefer to use \n instead
#include <iostream> // for std::cout
 
int main()
{
    int x{ 5 };
    std::cout << "x is equal to: " << x << '\n'; // single quoted (by itself) (conventional)
    std::cout << "Yep." << "\n";                 // double quoted (by itself) (unconventional but okay)
    std::cout << "And that's all, folks!\n";     // between double quotes in existing text (conventional)
    return 0;
}

try to prefer 2nd or 3rd options

1.5.3 - std::cin

For advanced readers

The C++ I/O library does not provide a way to accept keyboard input without the user having to press enter. If this is something you desire, you’ll have to use a third party library. For console applications, we’d recommend pdcurses, FXTUI, cpp-terminal, or notcurses. Many graphical user interface libraries have their own functions to do this kind of thing.

is buffered

The basic extraction process

Here’s a simplified view of how operator >> works for input.

  1. If std::cin is not in a good state (e.g. the prior extraction failed and std::cin has not yet been cleared), no extraction is attempted, and the extraction process aborts immediately.
  2. Leading whitespace characters (spaces, tabs, and newlines at the front of the buffer) are discarded from the input buffer. This will discard an unextracted newline character remaining from a prior line of input.
  3. If the input buffer is now empty, operator >> will wait for the user to enter more data. Any leading whitespace is discarded from the entered data.
  4. operator >> then extracts as many consecutive characters as it can, until it encounters either a newline character (representing the end of the line of input) or a character that is not valid for the variable being extracted to.

The result of the extraction process is as follows:

  • If the extraction aborted in step 1, then no extraction attempt occurred. Nothing else happens.
  • If any characters were extracted in step 4 above, extraction is a success. The extracted characters are converted into a value that is then copy-assigned to the variable.
  • If no characters could be extracted in step 4 above, extraction has failed. The object being extracted to is copy-assigned the value 0 (as of C++11), and any future extractions will immediately fail (until std::cin is cleared).

For example, given the following snippet:

int x{};
std::cin >> x;

Here’s what happens in a three different input cases:

  • If the user types 5a and enter, 5a\n will be added to the buffer. 5 will be extracted, converted to an integer, and assigned to variable x. a\n will be left in the input buffer for the next extraction.
  • If the user types ‘b’ and enter, b\n would be added to the buffer. Because b is not a valid integer, no characters can be extracted, so this is an extraction failure. Variable x would be set to 0, and future extractions will fail until the input stream is cleared.
  • If std::cin is not in a good state due to a prior failed extraction, nothing happens here. The value of variable x is not altered.

Standard Streams

std::cout is essentially just the file descriptor of stdout in C++. In C it’s simply stdout and the POSIX definition is STDOUT_FILENO. Simple reinterpret_cast<std::ostream>(1) doesn’t work tho :(

similarily for std::cin and stdin

1.7 - Keywords and Naming Variables

[1] https://youtu.be/-J3wNP6u5YU?si=88RiPt65T8rncjeg

C++ reserves 92 keywords that you can’t use as variable names

C++ Keywords

- alignas
- alignof
- and
- and_eq
- asm
- auto
- bitand
- bitor
- bool
- break
- case
- catch
- char
- char8_t (since C++20)
- char16_t
- char32_t
- class
- compl
- concept (since C++20)
- const
- consteval (since C++20)
- constexpr
- constinit (since C++20)
- const_cast
- continue
- co_await (since C++20)
- co_return (since C++20)
- co_yield (since C++20)
- decltype
- default
- delete
- do
- double
- dynamic_cast
- else
- enum
- explicit
- export
- extern
- false
- float
- for
- friend
- goto
- if
- inline
- int
- long
- mutable
- namespace
- new
- noexcept
- not
- not_eq
- nullptr
- operator
- or
- or_eq
- private
- protected
- public
- register
- reinterpret_cast
- requires (since C++20)
- return
- short
- signed
- sizeof
- static
- static_assert
- static_cast
- struct
- switch
- template
- this
- thread_local
- throw
- true
- try
- typedef
- typeid
- typename
- union
- unsigned
- using
- virtual
- void
- volatile
- wchar_t
- while
- xor
- xor_eq

**Variable Naming Rules**
  • name can not be a keyword
  • only be alphanumeric + -
  • can not begin with a number
  • case sensitive
int value; // conventional
 
int Value; // unconventional (should start with lower case letter)
int VALUE; // unconventional (should start with lower case letter and be in all lower case)
int VaLuE; // unconventional (see your psychiatrist) ;)
int my_variable_name;   // conventional (separated by underscores/snake_case)
int my_function_name(); // conventional (separated by underscores/snake_case)
 
int myVariableName;     // conventional (intercapped/camelCase)
int myFunctionName();   // conventional (intercapped/camelCase)
 
int my variable name;   // invalid (whitespace not allowed)
int my function name(); // invalid (whitespace not allowed)
 
int MyVariableName;     // unconventional (should start with lower case letter)
int MyFunctionName();   // unconventional (should start with lower case letter)

1.9 - Introduction to literals and operators

NOTE

For the operators we call primarily for their side effects (e.g. operator= or operator<<), it’s not always obvious what return values they produce (if any). For example, what return value would you expect x = 5 to have?

Both operator= and operator<< (when used to output values to the console) return their left operand. Thus, x = 5 returns x, and std::cout << 5 returns std::cout. This is done so that these operators can be chained.

For example, x = y = 5 evaluates as x = (y = 5). First y = 5 assigns 5 to y. This operation then returns y, which can then be assigned to x.

std::cout << "Hello " << "world!" evaluates as (std::cout << "Hello ") << "world!". This first prints "Hello " to the console. This operation returns std::cout, which can then be used to print "world!" to the console as well.