Programming in C++

Fast Track to C++ Syntax for (C) Programmers

Gerald Senarclens de Grancy

Compiling a Basic Program

#include <iostream>

/* Hello World program written in C++. */
int main(void) {
  std::cout << "Hello, World!" << std::endl;  // output some text
  return 0;  // 0 means success
}

Download hello.cpp

Compilation can be done with either clang++ or g++ (part of the GNU Compiler Collection, GCC).

clang++ $INFILE -o $PROGRAM_NAME
g++ $INFILE -o $PROGRAM_NAME

I/O

Writing to Standard Output

While <stdio.h> (printf, scanf etc.) is available, C++ allows using I/O streams

#include <iostream>

/* Hello World program written in C++. */
int main(void) {
  std::cout << "Hello, World!" << std::endl;  // output some text
  return 0;  // 0 means success
}

Visualize execution

Reading Input

#include <iostream>  // allows using std::cout, std::cin and std::endl

// main function - C++ programs start their execution here
int main(void) {
  int number;
  std::cout << "Please enter an integer: ";  // writing to stdout
  std::cin >> number;  // reading user input
  std::cout << "You've entered " << number << std::endl;
  return 0;  // indicate successful termination to environment
}  // blocks are delimited with curly braces

Download input.cpp

Variables and Data Types

C++ has a native bool data type

C++ also offers constructor initialization () and uniform initialization {}

#include <iostream>

int main() {
  bool b = true;  // c-like initialization
  char c('x');  // constructor initialization (only valid during declaration)
  int i {-5};  // uniform initialization (only valid during declaration)
  std::cout << b << std::endl;
  std::cout << c << std::endl;
  std::cout << i << std::endl;
  return 0;
}

Visualize execution

Block Scope

Identifiers are only valid in their defining block

#include <iostream>

int main() {
  {
    int number = 3;
  }
  // std::cout << number << std::endl;  // error: use of undeclared identifier
  return 0;
}

Download block_scope.cpp

C++ offers type deduction: auto and decltype

Both should only be used to increase readability

#include <iostream>

int main() {
  bool b1(true);
  auto b2 = not b1;  // `bool b2` deducted from assignment
  char c1(65);
  decltype(c1) c2;  // `char c2` deducted without assignment
  c2 = 66;
  std::cout << b1 << " " << b2 << std::endl;
  std::cout << c1 << " " << c2 << std::endl;
  return 0;
}

Visualize execution

References

C++ supports references which are more programmer friendly than pointers

#include <iostream>

int main(void) {
  int j = 3;
  int k = 100;
  int* i = &j;  // C-style pointer
  int& m = j;  // C++ reference
  int& n = m;  // also references j
  n = 2;
  m = k;  // value assignment (references always "point" to the same memory)

  std::cout << "i: " << (*i) << std::endl;
  std::cout << "m: " << m << std::endl;
  std::cout << "n: " << n << std::endl;
}

Visualize execution

The C++ standard library offers container data types

#include <iostream>
#include <string>
#include <vector>

int main() {
  char sep('\t');  // single character (escaped tab key)
  std::string name("Amelie");  // C++ string
  std::vector<double> numbers(3);  // a managed array of doubles
  std::cout << name << std::endl;
  for (int i = 0; i < numbers.size(); ++i) {
    std::cout << numbers[i] << sep;
  }
  std::cout << std::endl;
  return 0;
}

Visualize execution

Conditional Execution

if and else

if (expression) {
  // block
} else if (expression) {
  // block
} else {
  block
}

The ternary operator provides a shorthand

Type result = condition ? value_if_true : value_if_false;

Example: if and else

#include <iostream>

int main() {
  int guess = 5;  // assume it was entered by a user
  if (guess == 4) {  // guaranteed random guess, used a die
    std::cout << "You won!" << std::endl;
  } else {
    std::cout << "Better luck next time, loser!" << std::endl;
  }
  return 0;
}

Visualize execution

switch

The C++ switch statement jumps to the block of code matching the value of an expression.
It executes one or multiple code blocks among many alternatives.

switch (expression){
    case constant1:
      // statements
      break;  // without break, execution of the following blocks continues
    case constant2:
      // statements
      break;
    // ...
    default:
      // statements
}

Example: switch

#include <iostream>
int main(int argc, char* argv[]) {
  switch (argc) {  // switch evaluates an expression: (argc)
  case 1:  // if the result of the expression resolves to 1, jump here
    std::cout << "Only the command was entered." << std::endl;
    break;  // break - jump out of the 'switch' block to avoid falltrough
  case 2:
    std::cout << "Command plus one argument" << std::endl;
    break;
  case 3:
    std::cout << "Command plus two arguments" << std::endl;
    break;
  default:  // any other value of the expression jumps here
    std::cout << "Command plus " <<  argc-1 << " arguments" << std::endl; break;
  }
  return 0;
}

Visualize execution

while Loops

while (expression) {
  // block
}

do ... while: run code block at least once regardless of the expression

do {
  // block
} while (expression);

Example

#include <iostream>

int main() {
  int n = 10;
  while (n > 0) {  // execute block while expression evaluates to `true`
    std::cout << n << ", ";
    --n;  // avoid side effects in statement above
  }
  std::cout << "FIRE!" << std::endl;
  return 0;
}

Visualize execution

Example: Execute a block of code at least once

#include <iostream>

int main() {
  bool condition = false;
  do {
    std::cout << "Do this at least once." << std::endl;
  } while (condition);
  std::cout << "FIRE!" << std::endl;
  return 0;
}

Visualize execution

for Loops

for (initialization; condition; update_statement) {
  // block
}

initialization and update_statement are optional

Every for loop can be expressed as while loop

// initialization
for (; condition; ) {
  // block
  // update_statement;
}

// initialization
while (condition) {
  // block
  // update_statement
}

Example

#include <iostream>
#include <string>
#include <vector>

int main() {
  std::vector<std::string> names = {"Pat", "Chris", "Sue", "Steve", "Anne"};
  for (std::vector<std::string>::size_type i(0); i < names.size(); ++i) {
    std::cout << names[i] << std::endl;
  }
  return 0;
}

Visualize execution

Iterators

C++ provides iterators (incl. reverse and const iterators)

for (auto it = container.begin(); it != container.end(); ++it) {
  // block using `*it`
}

Example: Iterators

#include <iostream>
#include <string>
#include <vector>

int main() {
  std::vector<std::string> names = {"Pat", "Chris", "Sue", "Steve", "Anne"};
  // use reverse iterators
  for (auto it = names.crbegin(); it != names.crend(); it++) {
    std::cout << *it << std::endl;  // dereference the iterator
  }
  std::cout << std::endl;
  return 0;
}

Visualize execution

Range-Based For Loops

C++ provides easy access each element in a container

for (const auto& element : container) {
   // block
}

Example: Perform Action on Each Element

#include <iostream>
#include <string>
#include <vector>

using std::cout;

int main() {
  std::vector<std::string> names = {"Pat", "Chris", "Sue", "Steve", "Anne"};
  for (const auto& name : names) {
    cout << name << " ";
  }
  cout << "\n";
}

Visualize execution

Function Signature

aka. type signature or type annotation

Defines the data types of the parameters and return value

For example, a function that returns the sum of two integers:

(int)(int, int)

Function Declaration

aka. function prototype or function interface

  • Specifies the function name and type signature, but omits the body
  • Required for using functions that are defined elsewhere
  • Promise to the compiler that the function will exist when linking

For example, a function that returns the sum of two integers:

int sum(int a, int b);

The parameter names are optional:

int sum(int, int);

Function Definition

  • Adds the function body to the declaration
  • The signature and name must exactly match the declaration
Type function_name(Type parameter1, Type parameter2, ...) { body }

For example, a function that returns the sum of two integers:

int sum(int a, int b) {
  return a + b;
}

Pass by Value

By default, C++ copies argument values to function parameters

#include <iostream>

void pass_by_value(int i) {
  i = 5;
  std::cout << "i: " << i << " (at the end of the called function)" << std::endl;
}

int main() {
   int i = 3;
   std::cout << "i: " << i << std::endl;
   pass_by_value(i);
   std::cout << "i: " << i << " (after returning from function)" << std::endl;
   return 0;
}

Visualize execution

Pass by Reference

C++ allows passing references to arguments

#include <iostream>

void pass_by_reference(int& i) {
  i = 5;
  std::cout << "i: " << i << " (at the end of the called function)" << std::endl;
}

int main() {
  int i = 3;
  std::cout << "i: " << i << std::endl;
  pass_by_reference(i);
  std::cout << "i: " << i << " (after returning from function)" << std::endl;
  return 0;
}

Visualize execution

Default Parameters

The declaration may contain default parameters.

#include <iostream>

void increment(int& i, int value = 1);

int main(void) {
  int num {5};
  increment(num, 5);
  increment(num);
  increment(num);
  std::cout << "num: " << num << std::endl;
}

void increment(int& i, int value) {
  i += value;
}

Visualize execution

Function Overloading

C++ allows function names to be used in multiple signatures

#include <iostream>
void print_array(int array[], size_t count);  // function declaration
void print_array(double* array, size_t count);  // overload function

int main(void) {
  int a1[] {1, 2, 3, 4};
  double a2[] {3.7, 9.4, 1.1, 12.9, -0.3};
  print_array(a1, 4);
  print_array(a2, 5);
  return 0;
}
void print_array(int array[], size_t count) {
  for (size_t i = 0; i < count; ++i) {
    std::cout << array[i] << std::endl;
  }
}
void print_array(double* array, size_t count) {
  while (count) {
    std::cout << *array << std::endl;
    ++array;
    --count;
  }
}

Visualize execution

Function Templates

C++ functions can operate with generic types to avoid code duplication

#include <iostream>
template <class Type>  // compiler will generate a function for required types
void print_array(Type* array, size_t count);
int main(void) {
  int a1[] {1, 2, 3, 4};
  double a2[] {3.7, 9.4, 1.1, 12.9, -0.3};
  print_array(a1, 4);
  print_array(a2, 5);
  return 0;
}
template <class Type>
void print_array(Type* array, size_t count) {
  while (count) {
    std::cout << *array << std::endl;
    ++array;
    --count;
  }
}

Visualize execution

Namespaces

C++ handles namespaces explicitly.

It is possible to avoid constantly prepending a namespace with the using keyword.

#include <iostream>

using std::cout;
using std::endl;

int main(void) {
  cout << "Namespaces are a honking great thing." << endl;
  return 0;
}

Visualize execution

Use your own namespace

#include <iostream>

namespace ns {
  void say_hello();
}  // namespace ns

int main(void) {
  ns::say_hello();
}

namespace ns {
  void say_hello() {
    std::cout << "Hello!" << std::endl;
  }
}  // namespace ns

Visualize execution

Avoid name collisions

#include <iostream>

namespace a {
  void say_hello() {
    std::cout << "Hello!" << std::endl;
  }
}  // namespace a
namespace b {
  void say_hello() {
    std::cout << "Bonjour!" << std::endl;
  }
}  // namespace b

int main(void) {
  a::say_hello();
  b::say_hello();
}

Visualize execution

Resources

C++ has too much more to offer to stuff into a single presentation. Good places to start looking for more are

Questions
and feedback...