Skip to content

Instantly share code, notes, and snippets.

@jreuben11
Last active October 12, 2023 20:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jreuben11/a3893971bf057dd81677838f8336b937 to your computer and use it in GitHub Desktop.
Save jreuben11/a3893971bf057dd81677838f8336b937 to your computer and use it in GitHub Desktop.
C++11 in a nutshell

C++11 in a nutshell

Modern C++ Coding Style

This post is based upon the standard - not supported with VC compiler ! Try these techniques using GCC / LLVM compilers and QtCreator / CLion IDEs

Avoid C programming style idioms

  • = rather than strcpy() for copying , == rather than strcmp() for comparing
  • use const , constexpr functions rather than #DEFINE, macros
  • Avoid void*, union, and raw casts (use static_cast instead)
  • use std:string instead of char*
  • avoid pointer arithmetic
  • To obey C linkage conventions, a C++ function must be declared to have C linkage

Avoid C# / Java programming style idioms

  • Minimize the use of reference and pointer variables: use local and member variables
  • Use abstract classes as interfaces to class hierarchies; avoid “brittle base classes,” that is, base classes with data members.
  • Use scoped resource management (“Resource Acquisition Is Initialization”; RAII)
  • Use constructors to establish class invariants (and throw an exception if it can’t)
  • Avoid “naked” new and delete; instead, use containers (e.g., vector, string, and map) and handle classes (e.g., lock and unique_ptr).
  • minimal run-time reflection: dynamic_cast and typeid
  • Namespaces are non-modular - headers can introduce name clashes - avoid using directives in header files, because it increases the chances of name clashes

C++11 new features

  • Constructor Controls =delete and =default
  • type deduction - auto
  • constant expression compile time evaluation - constexpr
  • In-class member initializers
  • Inheriting constructors
struct derived : base {
   using base::base; // instead of deriving multiple constructor sigs
};
  • Lambda expressions
  • Move semantics
  • specify function may not throw exceptions - noexcept
  • A proper name for the null pointer - nullptr (instead of NULL)
  • The range-for statement
  • Override controls: final and override
  • Template Type aliases - binding arguments of another template
template<class T> struct Alloc {};
template<class T> using Vec = vector<T, Alloc<T>>;
// type-id is vector<T, Alloc<T>>
Vec<int> v; // Vec<int> is the same as vector<int, Alloc<int>>
  • Typed and scoped enumerations: enum class
  • Universal and uniform initialization
  • Variadic templates
  • Hashed containers, such as unordered_map
  • basic concurrency library components: thread, mutex, and lock
  • asynchronous computation: future, promise, and async()
  • regexp
  • unique_ptr, shared_ptr
  • tuple
  • bind(), function
  • decltype(expr) - reuse a type

Basics

The stream idiom

  • overloading IO stream ops:
ostream& operator<<(ostream& os, const Entry& e)
istream& operator>>(istream& is, Entry& e)
  • cout << , cin >> - stdio streams
  • getline() - helper function for making it easier to read from stdin
  • If a pointer to function is given as the second argument to <<, the function pointed to is called. cout<<pf means pf(cout). Such a function is called a manipulator
  • <sstream> istringstream - A stream that reads from a string
  • c_str() returns a C-style pointer to a zero-terminated array of characters

determine the memory footprint of a variable, or the size of an array

sizeof(T)
extent <decltype( T )> ::value
  • size of an array must be a constant expression

Utilize initializer lists, auto, constexpr

  • use initializer lists {} instead of =, () - prevents narrowing conversions
  • with auto, need to use =
  • prefer constexpr to const - compile time evaluation

iterating over a container:

  • v.begin() and v.end() or begin(v) and end(v)
  • for (const auto& x : v) - for each x in v
  • for (int& x : v) - to modify an element in a range-for loop, the element variable should be a reference

Mechanisms: pass by ref, const functions, operator overloading, subscripting

  • pass by ref
void doSomething(MyClass& c) {}
  • const function:
double real() const { return re; }
  • operator overload: add to re and im
complex& operator+=(complex z) {
  re+=z.re, im+=z.im;
  return * this;
}
  • element access: subscripting
double& operator[](int i) { return elem[i]; }

A common pattern: templatized RAII Handles as custom container wrappers

template<typename T>
class Handle {          // Parameterized Type
  private:
    T* elem;
...
Handle(int s) :elem{new double[s]}, sz{s}  { ... } // constructor: acquire resources, initializer_list<T>
~Handle() { delete[] elem; }                       // destructor: release resources
virtual double& operator[](int) = 0;     // pure virtual function
void doSomething(vector<Item*>& v, int x) { //  do something to all items
  for (auto p : v)
    p–>doSomething(x);
}

Optimal techniques to pass arguments and retvals

  • pass & return references & from functions.
  • never return a pointer *. can return a smart pointer.
  • Return containers by value (relying on move for efficiency)
  • Each time a function is called, a new copy of its arguments is created.
  • a pointer or a reference to a local non-static variable should never be returned.
  • non-primitive arguments should be passed as const &
  • For template arguments, an rvalue reference is used for “perfect forwarding”
  • Default arguments may be provided for trailing arguments only
  • the recommended way to pass containers in and out of functions: pass Containers by reference, return them as objects by value (implicitly uses move)

range-for over a handle

  • To support the range-for loop for a container handle, must define suitable begin() and end() functions:
template<typename T>
T* begin(Handle<T>& x)
{}

functors, lambdas, variadic templates

  • functor: call operator
bool operator()(const T& x) const { return x<val; }
  • bind - forward call wrapper
  • lambda - eg capture all by ref
[&](const string& a){ return a<s; }
  • variadic template
template<typename T, typename... Tail>
void f(T head, Tail... tail)

ISO C++ standard types

  • character types: char (8), [signed | unsigned ] char, wchar_t (unicode), char16_t (UTF), char32_t. L'AB'
  • <cctype>:: - isspace(), isalpha(), isdigit(), isalnum()
  • integer types: [signed | unsigned] short | int | long | long long
  • ::size_t x = sizeof(xxx) ; - implementation defined type
  • query arithmetic type properties: <limits>::numeric_limits

initialization gotchas – narrowing, default values, initialization lists

  • error: narrowing bool b2 {7};
  • {} indicates default value (nullptr for pointers)
  • if (p) { - equivalent to p != nullptr
  • auto - use =, because {} is deduced to std::initializer_list<T>
  • The only really good case for an uninitialized variable is a large input buffer.
  • only stack variables are default initialized !

function pointers

  • pointer to function taking a double and returning an int:
    using PF = int( * )(double);  
  • beware: cannot dereference or increment void*
void (* pf)(string)  = &DoSomething;
pf("blah");
using F = void( * )(string);

problems with passing arrays as arguments

  • arrays cannot be copied, assigned, or passed by val
  • from <string.h> - the size of the array is lost to the called function: extern "C" int strlen(const char *);
  • an argument of type T[] will be converted to a T* by val - preferable to pass a reference to some container

Prefer references

  • must be initialized from an object / primitive, read only, object like syntax, used for passing by ref params / retvals
int var = 0;
int& rr {var};
++rr;               // var is incremented by 1
int* pp = &rr;      // pp points to var
  • move uses “ref ref”: move(x) means static_cast<X&&>(x) where X& is the type of x.

Simple constructs:

  • struct, union, enum, enum class
  • enum class vs enum
enum class Color { red, blue, green };  
auto col = Color::red;   
  • struct vs class - struct can be initialized using the {} notation - order is important , no need for constructor. all members public.

function signature keywords

[[noreturn]] virtual inline auto f(const unsigned long int *const) –> void const noexcept;
  • avoid argument conversions for params: explicit - prevent implicit conversions
  • 2 ways to specify an inline function: A) A member function defined within the class declaration is taken to be an inline member function. B) Use inline keyword
  • declare a static function - keyword static in declaration is not repeated in the definition eg
template<class T, class U>
auto product(const vector<T>& x, const vector<U>& y) –> decltype(x*y);

What makes a function signature unique

  • unique function signature check ignores const in params
  • Function argument names are not part of the function type
  • an argument is unused in a function definition by not naming it

Error handling

gracefully / ungracefully halting execution

  • if (something_wrong) exit(1);
  • terminate() - does not invoke destructors. calls abort()
  • std::set_terminate()
  • quick_exit() , at_quick_exit()

basic guarantee vs strong guarantee

  • basic guarantee for all ops: invariants of all objects are maintained, and no resources are leaked
  • strong guarantee for key ops: either the operation succeeds, or it has no effect
  • nothrow guarantee for some operations

handling errors

  • errno vs <stdexcept>
  • throw ex
  • throw; - rethrow
  • catch (std::exception& err) {
  • catch (...)

Catch common exceptions in main

catch (...) {
  cerr << "x"
}

the standard exception hierarchy

  • exception: logic_error, runtime_error, bad_exception, bad_alloc, bad_cast, bad_typeid
  • logic_error: length_error, domain_error, invalid_argument, out_of_range, future_error
  • runtime_error: overflow_error, range_error, underflow_error, system_error, regex_error, ios_base::failure

useful members of the exception class

  • exception::what()
  • current_exception()
  • rethrow_exception()
  • make_exception_ptr()
  • exception_ptr
  • nested_exception
  • terminate()

best practices for handling exceptions

  • catch exception&
  • destructors do not throw
  • can throw runtime_error in ctor
  • an exception is potentially copied several times up the stack before it is caught - don’t put huge amounts of data in exception body
  • innocent-looking operations (such as <, =, and sort()) might throw exceptions
  • error in a thread can crash a whole program - add a catch-all handler to main()
  • The body of a function can be a try-block.
  • handle RAII is neater than try catch finally

assertions

  • compile-time static_assert vs runtime macro <cassert> assert

handling exceptions in multithreaded code

  • packaged_task does the following:
catch(...) { myPromise.set_exception(current_exception()); }

Source Files and Programs

the C++ compilation and linkage process

  • A program is a collection of separately compiled units combined by a linker
  • file is unit of compilation
  • precompiler --> translation unit
  • A program must contain exactly one function called main()
  • An entity must be defined exactly once in a program. It may be declared many times, but the types must agree exactly.
  • Outside a class body, an entity must be declared before it is used
  • static vs shared libraries

include semantics

  • #include <iostream> - from standard include directory
  • #include "myheader.h" - from current directory

external linkage

  • external linkage - when definition is not in same TU as declaration, use extern for external linkage
#ifdef __cplusplus
extern "C" {}   // a linkage block
#endif
  • using, constexpr, static & const not accessible from other TUs - hence extern const

Defining macros

#define PRINT(a,b) cout<<(a)<<(b)
  • concatenate: Writing #x " = " rather than #x << " = " is obscure “clever code”
  • predefined macros: __FUNC__ , __FILE__ , __LINE__

best practices for reducing compile time

  • Precompiled headers - modern C++ implementations provide precompiling of header files to minimize repeated compilation of the same header.
  • avoid global variables
  • a header should never contain: ordinary function definitions, fields, global variables, unnamed namespaces, using directives
  • To ensure consistency (identical definition), place using x= aliases, consts, constexprs, and inlines in header files
  • dont abuse #include
  • minimize dependencies
  • compiler include guards
  • An unnamed namespace {} can be used to make names local to a compilation unit - internal linkage

Classes and OOP

class default methods

  • by default a class provides 6 default methods
class X {
  X();                     // default constructor
  X(const X&);             // copy constructor
  X(X&&);                  // move constructor
  X& operator=(const X&);  // copy assignment: clean up target and copy
  X& operator=(X&&);       // move assignment: clean up target and move
  ~X();                    // destructor: clean up
}
X x = new X();

When to customise the default functions of a class:

  • If a class has a virtual function, it needs a virtual destructor
  • If a class has a reference member, it probably needs copy operations
  • If a class is a resource handle, it probably needs copy and move operations, constructor, a destructor, and non-default copy operations
  • If a class has a pointer member, it probably needs a destructor and non-default copy operations
  • Don't need a copy constructor just to copy an object - objects can by default be copied by assignment. default semantics is memberwise copy.

default and delete functions

  • =default and =delete
  • if you implement custom class, for default functions that are default, specify =default;
  • prevent destruction of an object by declaring its destructor =delete (or private)
  • prevent slicing - Prohibit copying of the base class: delete the copy operations.

constructor call order:

  • be aware that constructor is called on every copy and move
  • delegating constructor - when one constructor calls another
  • Constructors execute member and base constructors in declaration order (not the order of initializers)
  • A constructor can initialize members and bases of its class, but not members or bases of its members or bases
  • Prevent conversion of a pointer-to-a-derived to a pointer-to-a-base: make the base class a protected base

Defining a class

  • constructor initialization lists
  • const functions and mutable fields
  • friend class / functions
  • defining an abstract base class
  • abstract base class definition (.cpp): virtual ~Base() = 0;

Nested Classes

  • A nested class has access to non-static members of its enclosing class, even to private members (just as a member function has), but has no notion of a current object of the enclosing class.
  • A class does not have any special access rights to the members of its nested class

different types of initialization: default, copy, memberwise

  • default initialization MyClass o1 {};
  • memberwise initialization MyClass o2 {"aaa", 77 };
  • copy initialization MyClass o3 { o2 };

Operator Overloading

  • stream out operator:
ostream& operator<<(ostream&, const string&);
  • calling std::cout << s; is the same as operator<<(std::cout, s);
  • overload the + and += operators for a Complex number type:
complex operator+(complex a, complex b) {
  return a += b; // access representation through +=.  
  // note: arguments passed by value, so a+b does not modify its operands.

inline complex& complex::operator+=(complex a) {
  re += a.re;
  im += a.im;
  return * this;
}
  • define a conversion operator: (resembles a constructor)
MyClass::operator int() const { return i; }      
  • declaring an indexer
const MyClass& operator[] (const int&) const;
  • declaring a functor:
int operator()(int);
pair<int,int> operator()(int,int);
  • smart pointers overload operator –>, new
  • increment / decrement operators:
Ptr& operator++();             // prefix
Ptr operator++(int);           // postfix
Ptr& operator––();             // prefix
Ptr operator––(int);           // postfix
  • allocator / deallocator operators - implicitly static
void* operator new(size_t);               // use for individual object
void* operator new[](size_t);             // use for array
void operator delete(void*, size_t);      // use for individual object
void operator delete[](void*, size_t);    // use for array
  • user defined literal operator:
constexpr complex<double> operator"" i(long double d) ;

the order of construction / destruction in a class hierarchy

  • Objects are constructed from the base up and destroyed top-down from derived
  • destructors in a hierarchy need to be virtual
  • the constructor of every virtual base is invoked (implicitly or explicitly) from the constructor for the complete object
  • destructors are simply invoked in reverse order of construction - a destructor for a virtual base is invoked exactly once.

keywords virtual, override, final:

  • keywords virtual, override, final should only appear in .h files
  • override and final are compiler hints
  • override specifier comes last in a declaration.
  • virtual functions - vtbl efficiency within 75%
  • final - can make every virtual member function of a class final - add final after the class name
class Derived final : public Base {

Scope rules for deriving from a base class

  • deriving from a base class can be declared private, protected, or public
  • If B is a private base, its public and protected members can be used only by member functions and friends of D. Only friends and members of D can convert a D* to a B*
  • If B is a protected base, its public and protected members can be used only by member functions and friends of D and by member functions and friends of classes derived from D. Only friends and members of D and friends and members of classes derived from D can convert a D* to a B*.
  • If B is a public base, its public members can be used by any function. In addition, its protected members can be used by members and friends of D and members and friends of classes derived from D. Any function can convert a D* to a B*.

Best practices for abstract classes and virtual functions

  • An abstract class should have a virtual destructor
  • A class with a virtual function should have a virtual destructor;
  • An abstract class typically doesn’t need a constructor

Best practices for declaring interfaces and data members

  • Prefer public members for interfaces;
  • Use protected members only carefully when really needed; a protected interface should contain only functions, types, and constants.
  • Don’t declare data members protected; Data members are better kept private & in the derived classes to match specific requirements.

Best practices deriving from a base class

  • Don’t call virtual functions during construction or destruction
  • Copy constructors of classes in a hierarchy should be used with care (if at all) to avoid slicing
  • A derived class can override new_expr() and/or clone() to return an object of its own type
  • covariant return rule - Return Type Relaxation applies only to return types that are pointers or references
  • a change in the size of a base class requires a recompilation of all derived classes

Almost Reflection

RTTI

  • <typeinfo> type_info typeid(x)
  • typ_info.name()
  • size_t typ_info.hash_code()

type traits to inspect code elements

  • <type_traits>
  • eg is_class<X> , is_integral<X>
  • eg
static_assert(std::is_floating_point<T>::value ,"FP type expected");
  • Add_pointer<T>
  • enable_if and conditional,
  • declval<X>

Casting

different types of casts:

  • const_cast for getting write access to something declared const
  • static_cast for reversing a well-defined implicit conversion
  • reinterpret_cast for changing the meaning of bit patterns
  • dynamic_cast - is run-time checked - it supports polymorphic class hierarchies

limits of dynamic casting

  • dynamic_cast used an upcast - returns nullptr otherwise.
if (auto px = dynamic_cast<MyClass*>(py)){}
  • dont use a ref: dynamic_cast<MyClass&> - could throw a bad_cast
  • dynamic_cast cannot cast from a void*. For that, a static_cast is needed
  • crosscasting, dynamic dispatch. better to use visitor pattern

upcasting vs downcasting

class Manager : public Employee {}

void g(Manager mm, Employee ee) {
  Employee* pe = &mm;               // OK: every Manager is an Employee
  Manager* pm = &ee;                // error: not every Employee is a Manager
  pm–>level = 2;                    // error: ee doesn't have a level
  pm = static_cast<Manager*>(pe);   // brute force: works
}

void Manager::doSomething() const {
  Employee:: doSomething();      //       base method
  doSomething();                // error
}

Custom Allocators

  • explicitly calling new: placement new - edge case where we specify memory address
void C::push_back(const X& a){
  new(p) X{a};   //  copy construct an X with the value a in address p
}
void C::pop_back() {
  p–>~X();      // destroy the X in address p
}

explicitly overloading allocators / deallocators

  • explicit:
  • new(&s) string{"xxx"}; - placement new: explicitly construct string
  • s.~string(); - explicitly destroy string
  • for enum class, define operator|() , operator&()
  • allocator<T>

Templates

Advantages of Templates

  • faster & easier to inline - no casting, or double dispatch
  • code for a member function of a class template is only generated if that member is used
  • Use templates to express containers
  • excellent opportunities for inlining, pass constant values as arguments --> compile-time computation.
  • resembles dynamic programming with zero runtime cost

Limitations of templates

  • no way to specify generic constraints ... until concepts. so errors that relate to the use of template parameters cannot be detected until the template is used
  • A member template cannot be virtual
  • copy constructors, copy assignments, move constructors, and move assignments must be defined as non-template operators or the default versions will be generated.
  • A virtual function member cannot be a template member function
  • Source code organization: #include the .cpp definition of the templates in every relevant translation unit --> long compile times
  • caution is recommended when mixing object-oriented and generic techniques - can lead to massive code bloat for virtual functions

creating and using a template

template<typename C>
class MyClass { };

MyClass<string> o;      // called a specialization
  • Members of a template class are themselves templates parameterized by the parameters of their template class.
template<typename C>
MyClass<C>::MyClass() {}        // ctor

template compile-time polymorphism

template<typename T>
void sort(vector<T>&);          // declaration
void f(vector<int>& vi, vector<string>& vs) {
  sort(vi);  // sort(vector<int>&);
  sort(vs);  // sort(vector<string>&);
}

Function Template Arguments

  • Use function templates to deduce class template argument types
template<typename T, int max>
struct MyClass {};

template<typename T, int max>
void f1(MyClass<T,int>& o);

void f2(MyClass<string,128>& o){
  f1(o);      // compiler deduces type and non-type arguments from a call,
}
  • function template overloads
sqrt(2);     // sqrt<int>(int)
sqrt(2.0);   // sqrt(double)
sqrt(z);     // sqrt<double>(complex<double>)

SFINAE

  • A template substitution failure is not an error. It simply causes the template to be ignored;
  • use enable_if to conditionally remove functions from overload resolution

Template Aliases

  using IntVector = vector<int>;

Best practices for implementing Templates

  • If a class template should be copyable, give it a non-template copy constructor and a non-template copy assignment - likewise for move
  • Avoid nested types in templates unless they genuinely rely on every template parameter
  • Define a type as a member of a template only if it depends on all the class template’s arguments
  • lifting - design for multiple type arguments

Concepts Generic Constraints

  • Estd namespace – template constraints
  • Ordered<X>, Equality_comparable<T>, Destructible<T>, Copy_assignable<T>, etc
  • implement a concept as a constexpr templated function with static_assert
  • parameterized --> constraint checks

Passing parameters to templates

  • template Parameters can be template types, built in types, values, or other class templates!
  • literal types cannot be used as template value parameters; function templates cannot be used as template params
  • can specify default types !
template<typename Target =string, typename Source =string>
Target to(Source arg)       // convert Source to Target
{ ... }

auto x1 = to<string, double>(1.2);  // very explicit (and verbose)
auto x2 = to<string>(1.2);         // Source is deduced to double
auto x3 = to<>(1.2);               // Target is defaulted to string; Source is deduced to double
auto x4 = to(1.2);                 // the <> is redundant

partial specialization vs complete specialization

  • complete specialization - Specialize templates to optimize for important cases
  • partial specialization -> curbing code bloat - replicated code can cost megabytes of code space, so cut compile and link times dramatically
  • to declare a pointer to some templated class, the actual definition of a class is not needed:
        X* p;    // OK: no definition of X needed
  • one explicit instantiation for a specialization then use extern templates for its use in other TUs.

template<> argument deduction

  • template<> argument can be deduced from the function argument list, we need not specify it explicitly:
template<>
bool less(const char* a, const char* b)

The curiously recurring template pattern (CRTP)

  • a class X derives from a class template instantiation using X itself as template argument
template<class T>
class Base {
  // methods within Base can use template to access members of Derived
};

class Derived : public Base<Derived> { };
  • use for static polymorphism (generic meta-programming )

Template Metaprogramming

  • templates constitute a complete compile-time functional programming language
  • constexpr
  • it is achieved via type functions, iterator traits
  • is_polymorphic<T>
  • Conditional<(sizeof(int)>4),X,Y>{}(7); - make an X or a Y and call it
  • Scoped<T>, On_heap<T>
  • template<typename T>
  • using Holder = typename Obj_holder<T>::type;
  • is_pod<T>::value
  • iterator_traits
  • <type_traits>
  • Enable_if<Is_class<T>(),T>* operator–>(); - select a member (for classes only)
  • std::is_integral and std::is_floating_point

variadic templates

template<typename T, typename... Args>  // variadic template argument list: one or more arguments
void printf(const char* s, T value, Args... args)    // function argument list: two or more arguments
  • One of the major uses of variadic templates is forwarding from one function to another
  • pass-by-rvalue-reference of a deduced template argument type “forward the zero or more arguments from t.”
template<typename F, typename... T>
void call(F&& f, T&&... t) {
  f(forward<T>(t)...);
}
auto t = make_tuple("Hello tuple", 43, 3.15);
double d = get<2>(t);

Know the Standard-Library

  • <utility> operators & pairs
  • <tuple>
  • <type_traits>
  • <typeindex> use a type_info as a key or hashcode
  • <functional> function objects
  • <memory> resource management pointers
  • <scoped_allocator>
  • <ratio> compile time rational arithmetic
  • <chrono> time utilities
  • <iterator> begin, end
  • <algorithm>
  • <cstdlib> - bsearch(), qsort()
  • <exception> - exception class
  • <stdexcept> - standard exceptions
  • <cassert> - assert macro
  • <cerrno> C style error handling
  • <system_error> - system error support: error_code, error_category, system_error
  • <string> - strlen(), strcpy(),
  • <cctype> - character classification: atof() , atoi()
  • <cwctype> wide character classification
  • <cstring> C-style string functions
  • <cwchar> C-style wide char functions
  • <cstdlib> C-style alloc functions
  • <cuchar> C-style multibyte chars
  • <regex>
  • <iosfwd> forward declarations of IO facilities
  • <iostream>
  • <ios> - iostream bases
  • <streambuf> - stream buffers
  • <istream> - input stream template
  • <ostream> - output stream template
  • <iomanip> - Manipulator
  • <sstream> - streams to/from strings
  • <cctype> - char classification functions
  • <fstream> - streams to/from files
  • <cstdio> - printf family of IO
  • <cwchar> - printf family of IO for wide chars
  • <locale> - cultures
  • <clocale> - cultures C-style
  • <codecvt> - code conversion facets
  • <limits> - numerical limits
  • <climits> - C-style integral limits
  • <cfloat> - C-style float limits
  • <cstdint> - standard integer type names
  • <new> - dynamic memory management
  • <typeinfo> - RTTI
  • <exception>
  • <initializer_list>
  • <cstddef> - C-style language support sizeof(), size_t, ptrdiff_t, NULL
  • <cstdarg> - variable length function args
  • <csetjmp> - C-style stack unwinding: setjmp , longjmp
  • <cstdlib> - program termination , C-style math functions eg abs() , div()
  • <ctime> - system clock
  • <csignal> - C-style signal handling
  • <complex>
  • <valarray>
  • <numeric>
  • <cmath>
  • <random>
  • <atomic>
  • <condition_variable>
  • <future>
  • <mutex>
  • <thread>
  • <cinttypes> type aliases for integrals
  • <cstdbool> C _Bool, true, false
  • <ccomplex>
  • <cfenv> C floating point stuff
  • <ctgmath>

STL Containers

STL features:

  • concepts: sequence vs associative containers; adapters, iterators, algorithms, allocators
  • container adapters - eg stack, queue, deque - double ended queue
  • Iterators are a type of pointer used to separate algorithms and containers
  • reverse iterators: rbegin, rend
  • const iterators: cbegin, cend
  • push_back does a copy via back_inserter
  • forward_list - SLL
  • multiset / multimap - values can occur multiple times
  • unordered_X
  • Xstream_iterator
  • How to implement a range check vector – not sure if this is good idea …
override operator [] with at()
  • prefer standard algorithms over handwritten loops
  • accumulate - aggregator algo
  • valarray - matrix slicing
  • unordered_map / set; multimap / set
  • bitset - array of bool
  • pair<T,U> , tuple<W,X,Y,Z>

functions common to several containers:

  • less<T>
  • copy(), find(), sort(), merge(), cmp(), equiv(), swap() , binary_search(), splice()
  • size(), capacity()
  • at() - range checks
  • emplace - nice and terse move push_back
  • hash<T> - functor
  • efficiently moving items from one container to another
copy(c1,make_move_iterator(back_inserter(c2)));    // move strings from c1 into c2

Container and Algorithm requirements

  • To be an element type for a STL container, a type must provide copy or move operations;
  • arguments to STL algorithms are iterators, not containers
  • _if suffix is often used to indicate that an algorithm takes a predicate as an additional argument.

STL common algorithms:

  • lower_bound(), upper_bound(), iter_swap()
  • for_each(b,e,f)
  • sequence predicates: all_of(b,e,f) , any_of(b,e,f) , none_of(b,e,f)
  • count(b,e,v) , count_if(b,e,v,f)
  • isspace()
  • find_if(b,e,f)
  • equal(b,e,b2)
  • pair(p1,p2) = mismatch(b,e,b2)
  • search(b,e,b2,e2) - find a subsequence
  • transform(b,e,out,f)
  • copy(b,e,out), move(b,e,out)
  • copy_if(b,e,out,f)
  • unique, unique_copy
  • remove() and replace()
  • reverse()
  • rotate(), random_shuffle(), and partition() - separates into 2 parts
  • next_permutation(), prev_permutation() , is_permutation() - generate permutations of a sequence
  • fill(), generate_n(), uninitialized_copy, uninitialized_fill - assigning to and initializing elements of a sequence. (unitialized_ is for low level objects that are not initialized)
  • swap(), swap_ranges(), iter_swap()
  • sort(), stable_sort() - maintain order of equal elements, partial_sort()
  • binary_search() - search sequence that is pre-sorted
  • merge() - combine 2 pre-sorted sequences
  • set_union, set_intersection, set_difference
  • lexicographical_compare() - order words in dictionaries
  • min & max
  • Use for_each() and transform() only when there is no more-specific algorithm for a task

Iterating over a container

  • [b:e)
  • end points to the one-beyond-the-last element of the sequence.
  • Never read from or write to *end.
  • the empty sequence has begin==end;
while (b!=e) {       // use != rather than <
  // do something
  ++b;   // go to next element
}

Iterator operations

  • input (++ , read istream), output (++, write ostream), forward (++ rw), bidirectional(++, --), random access
  • iterator_traits - select among algorithms based on the type of an iterator
  • ++p is likely to be more efficient than p++.
  • 3 insert iterators:
  • insert_iterator - inserts before the element pointed to using insert().
  • front_insert_iterator - inserts before the first element of a sequence using push_front().
  • back_insert_iterator - inserts after the last element of the sequence using push_back().
  • Use base() to extract an iterator from a reverse_iterator

comparisons

  • T[N] vs array<T,N> (raw array vs array type) - Use array where you need a sequence with a constexpr size. Prefer array over built-in arrays
  • bitset<N> vs vector<bool> - Use bitset if you need N bits and N is not necessarily the number of bits in a built-in integer type. Avoid vector<bool>
  • pair<T,U> vs tuple<T...> - use make_pair() / make_tuple() for type deduction. tie() can be used to extract elements from a tuple as out params
  • basic_string<C>, valarray<T>

New Techniques: Lambdas, smart pointers and move

Mitigating common memory management problems

  • mem management problems: leaks, premature deletion, double deletion
  • int* p2 = p1; - potential trouble
  • handles, RAII, move semantics eliminate problems

function adapters

  • <functional> - function adaptors - take a function as argument and return a functor that can be used to invoke it
  • bind(f,args) , mem_fn(f) , not(f) - currying, partial evaluation. more easily expressed using lambdas. Uses _1 from std::placeholders
  • ref() - like bind(), but doesnt dereference early - use to pass references as arguments to threads because thread constructors are variadic templates. useful for callbacks, for passing operations as arguments, etc.
  • mem_fn() (or a lambda) can be used to convert the p–>f(a) calling convention into f(p,a)
  • function is specified with a specific return type and a specific argument type. Use function when you need a variable that can hold a variety of callable objects
int f(double);
function<int(double)> fct {f};  // initialize to f
fct = [](double d) { return round(d); };     // assign lambda to fct

Passing values in / out of Lambdas

  • lambda default is all by reference: [&]
  • [=] by value (copy). recommended for passing a lambda to another thread
  • mutable - capture values can change
  • for_each - just use ranged for
  • [&v...] variadic params can be captured
  • can capture the current object BY REFERENCE [this]
  • the minimal lambda expression is []{}
auto z4 = [=,y]()–>int { if (y) return 1; else return 2; }    // OK: explicit return type

Passing lambdas around

  • std::function<R(AL)> where R is the lambda’s return type and AL is its argument list of types - function<void(char* b, char* e)>
  • can store a lambda in a variable of type auto - no two lambdas have the same type

3 types of smart pointers in C++11

  • unique_ptr - note: there is no make_unique ... yet:
unique_ptr<X> sp {new X};

unique_ptr<int> f(unique_ptr<int> p) {
  ++ * p;
  return p;
}

void f2(const unique_ptr<int>& p) {
  ++ * p;
}

unique_ptr<int> p {new int{7}};
p=f(p);            // error: no copy constructor
p=f(move(p));      // transfer ownership there and back
f2(p);             // pass a reference
  • shared_ptr – create with make_shared() - eg a structure with two pointers: one to the object and one to the use count:
auto p = make_shared<MyStruct>(1,"Ankh Morpork",4.65);
  • weak_ptr - break loops in circular shared data structures
  • note: shared_ptr are copied , unique_ptr are moved
  • you obviously still need to know your pointer prefix operator overloads * , & :prefix unary * means “dereferenced contents of” and prefix unary & means “address of.”
  • Mixing containers and smart pointers: vector<unique_ptr<Item>> v; -rather than vector<Item*> or vector<Item>, because all items implicitly destroyed

move

  • STL containers & smart pointers leverage move operations, as should custom RAII handles: template<class T> class Handle { }
  • primitive types & their pointers are always copied, even when moved
  • move must leave the source object in a valid but unspecified state so its destructor can be called
  • a move cannot throw, whereas a copy might (because it may need to acquire a resource).
  • a move is often more efficient than a copy.
  • Because initializer_list elements are immutable, cannot apply a move constructor - use uninitialized_copy

copying vs moving

  • the diff between lvalues and rvalues: lvalue - named & unmovable VS rvalue - temporary value that is movable
  • copy constructors by default do memberwise copy - not good for containers !
  • A copy constructor and a copy assignment for a class X are typically declared to take an argument of type const X&
Item a2 {a1};                // copy initialization
Item a3 = a2;                // copy assignment
Handle(const Handle& a);             // copy constructor
Handle& operator=(const Handle  a);  // copy assignment
Handle(Handle&& a);               // move constructor
Handle& operator=(Handle&& a);          // move assignment
std::move(x)                    // explicit
=delete;        // supress default copy / move definitions

move vs forward

  • A move() is simply a cast to an rvalue: static_cast<Remove_reference<T>&&>(t);
  • forward() is for “perfect forwarding” of an argument from one function to another
  • <utility> swap()
  • <typeindex> comparing and hashing type_index (created from a type_info)

allocate / deallocate functions

  • p=a.allocate(n); - acquire space for n objects of type T
  • a.deallocate(p,n); - release space for n objects of type T pointed to by p

best practices for working with smart pointers

  • unique_ptr cannot be copied, but can be moved. When destroyed, its deleter is called to destroy the owned object.
  • Shared pointers in a multi-threaded environment can be expensive (because of the need to prevent data races on the use count).
  • A destructor for a shared object does not execute at a predictable time
  • Prefer resource handles with specific semantics to smart pointers
  • Prefer unique_ptr to shared_ptr.
  • Prefer ordinary scoped objects to objects on the heap owned by a unique_ptr
  • Minimize the use of weak_ptr
  • a weak_ptr can be converted to a shared_ptr using the member function lock():
if (auto q = p.lock()) {
} else {
  p.reset();
}

Basic Mathematics Support

query numeric limits

  • specializations of the numeric_limits template presented in <limits>
  • numeric_limits<unsigned char>::max()
  • <climits> - DBL_MAX and FLT_MAX C Macros

valarray efficient matrix operations

  • valarray - slices and strides for matrices
  • slice_array, gslice_array, mask_array, indirect_array
valarray<int> v {
  {00,01,02,03},    // row 0
  {10,11,12,13},    // row 1
  {20,21,22,23}     // row 2
};
  • slice{0,4,1} describes the first row of v (row 0)
  • slice{0,3,4} describes the first column (column 0)
  • gslice is a “generalized slice” that contains (almost) the information from n slices
  • accumulate() adds elements of a sequence using their + operator:
  • inner_product(), partial_sum() and adjacent_difference()
  • iota(b,e,n) assigns n+i to the ith element of [b:e).

generate random numbers

  • <random> - random_device, Rand_int , Rand_double
auto gen = bind(normal_distribution<double>{15,4.0},default_random_engine{});
for (int i=0; i<500; ++i) cout << gen();

Concurrency

different levels of concurrency and locking

  • atomic, thread, condition_variable, mutex , future + promise, packaged_task, and async()

Creating a worker thread

  • thread t {F{x}}; - pass a functor constructor to a thread --> thread executes functor
  • function arguments - const refs for RO, pointers for out params

Advanced Threading Constructs in C++11

  • advanced locking: defer_lock & explicit lock()
  • condition_variable - ::wait(), ::notify_one()
  • promise<T>::set_value(), future<T>::get
  • packaged_task<Task_Type> ::get_future()
  • async::get
  • thread_local

creating a critical section

mutex m; // used to protect access to shared data
void f() {
  unique_lock<mutex> lck {m}; // acquire the mutex m, automatically scoped
  // ... manipulate shared data ...
}

atomics

  • simplest memory order is sequentially consistent
  • Atomics - Lock-free programming: load and store
enum memory_order {
  memory_order_relaxed,
  memory_order_consume,
  memory_order_acquire,
  memory_order_release,
  memory_order_acq_rel,
  memory_order_seq_cst
};

r1 = y.load(memory_order_relaxed);
x.store(r1,memory_order_relaxed);
  • [[carries_dependency]] - for transmitting memory order dependencies across function calls
  • kill_dependency() - for stopping the propagation of such dependencies.
  • atomic<T> - compare_exchange_weak(), compare_exchange_strong(), is_lock_free()
  • 2 possible values of an atomic_flag are called set and clear.
  • atomic_thread_fence, atomic_signal_fence - barriers
  • volatile

simple multi-threaded programming

  • thread::join, joinable, swap, detach
thread my_thread2 {my_task,ref(v)};       // OK: pass v by reference

this_thread::sleep_until(tp);
this_thread::sleep_for(d);
this_thread::sleep_for(milliseconds{10});
this_thread::yield();
  • thread_local - thread local storage
  • thread::hardware_concurrency() - reports the number of tasks that can simultaneously proceed with hardware support
  • thread::get_id() - get thread id

Threading gotchas

  • A thread is a type-safe interface to a system thread. After construction, a thread starts executing its task as soon as the run-time system can acquire resources for it to run. Think of that as “immediately.”
  • Do not destroy a running thread; Use join() to wait for a thread to complete
  • don’t pass pointers to your local data to other threads
  • beware of by-reference context bindings in lambdas
  • a thread can be moved but not copied
  • thread constructors are variadic templates - to pass a reference to a thread constructor, must use a reference wrapper

Locking

  • mutex - lock(), unlock(), try_lock()
  • recursive_mutex - can be repeatedly acquired without blocking
  • timed_mutex - try_lock_until(tp), try_lock_for(d)
  • recursive_timed_mutex
  • lock_guard<T> - guard for a mutex - simplest
  • unique_lock<T> - lock for a mutex - supports timed ops
  • A mutex cannot be copied or moved
  • lock_guard<mutex> g {mtx}; - destructor does the necessary unlock() on its argument.
  • owns_lock() - check whether an acquisition succeeded

Ensure that a function is executed only once across multiple threads

  • once_flag
  • call_once()

Signaling between threads

  • condition_variable - ::wait(), ::wait_until(), ::wait_for() , ::notify_one(), notify_all() - like a WaitHandle on a unique_lock<mutex>
  • condition_variable_any - like a condition_variable but can use any lockable object

The the promise-future paradigm for 'async callbacks'

  • A future is a handle to a shared state. It is where a task can retrieve a result deposited by a promise
  • The status of a future can be observed by calling wait_for() and wait_until(). the result can be retrieved via ::get()
        future<double> fd = async(square,2);
        double d = fd.get();
  • shared_future<T> - can read result multiple times
auto handle = async(task,args);
res = handle.get()                 // get the result
  • future_error exception with the error condition broken_promise
  • promise - ::set_value, ::set_exception
  • A packaged_task can be moved but not copied. ::get_future()
packaged_task<int(int)> pt1 {DoSomething};
pt1(1);
  • Don’t set_value() or set_exception() to a promise twice. Don’t get() twice from a future;
  • Use async() to launch simple tasks

C++ is not C

IO and strings The C way:

  • <stdio> - fopen(), fclose(), mode flag
  • printf(), fprintf(), sprintf()
  • stdin, stdout, stderr
  • scanf()
  • getc(), putc(), getchar(), putchar()
  • <string.h> - strlen(), strcpy(), strcat(), strcmp(), strstr()
  • atoi

IO and strings The C++ way: streams

  • <istream>, <ostream>
  • iostream
  • Forward declarations for stream types and stream objects are provided in <iosfwd>
  • cin, cout, cerr, clog
  • <fstream> - ifstream, ofstream, fstream
  • <sstream> - istringstream, ostringstream, stringstream
  • ios::ate - write at end
  • use str() to read a result
  • state functions: good(), eof(), fail(), bad()
  • getline(), get(c)
  • put(), write(), flush()
  • noskipws
  • cin >> c1 >> x >> c2 >> y >> c3;
  • peek, seekg
ostream& operator<<(ostream& os, const Named_val& nv) {
  return os << '{' << nv.name << ':' << nv.value << '}';
}
  • basic_ios class manages the state of a stream: buffers, formatting, locales, Error handling
  • streambuf
  • istreambuf_iterator and ostreambuf_iterator
  • An ostreambuf_iterator writes a stream of characters to an ostream_buffer
  • sto* (String to) functions

Time The C way:

  • <ctime> - clock_t, time_t, tm

Time The C++ way: duration

  • std::chrono namespace FROM <chrono>
  • high_resolution_clock, duration_cast<T>
  • duration, time_point
  • call now() for one of 3 clocks: system_clock, steady_clock, high_resolution_clock
  • duration_cast<milliseconds>(d).count()
  • ratio<1>
  • duration<si45, micro> -SI units from <ratio> constructed from ratio<int,int>

Some low level C features may still be handy in a C++ program

  • memory.h: memcpy(), memove(), memcmp(), memset(), calloc(), malloc(), realloc(), free()
  • cmp() , qsort() , bsort()
  • safe fast low level C style copy: if (is_pod<T>::value) memcpy( ...
  • initialize a char array:
char* p = L“…”;
"" == const char* p = '\0';
  • prefixes: L for wide chars, R for raw strings

Some best practice Takeaways:

  • using namespace std; - place in cpp files after includes to make your code more succinct
  • prefer {}-style initializers and using for type aliases
  • Use constructor/destructor pairs to simplify resource management (RAII).
  • Use containers and algorithms rather than built-in arrays and ad hoc code
  • Prefer standard-library facilities to locally developed code
  • Use exceptions, rather than error codes, to report errors that cannot be handled locally
  • Use move semantics to avoid copying large objects
  • Avoid “naked” new and delete. Use unique_ptr / shared_ptr
  • Use templates to maintain static type safety (eliminate casts) and avoid unnecessary use of class hierarchies
  • Header files - semi-independent code fragments minimize compilation times
  • Pointers - a fixed-size handle referring to a variable amount of data “elsewhere”
  • use namespaces for scope: namespace {}
  • establish class invariants in constructors (throw exceptions -> fail fast)
  • static_assert - only works on constant expressions (useful in generics)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment