Geeks With Blogs

Josh Reuben

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*, unions, 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

        void doSomething(MyClass& c) {} // pass by ref

        const function:         double real() const { return re; } 

        operator overload:      complex& operator+=(complex z) { re+=z.re, im+=z.im; return *this; } // add to re and im

        double& operator[](int i) { return elem[i]; } // element access: subscripting

    

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-primative 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

        bool operator()(const T& x) const { return x<val; } // call operator


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 char types 

  • character types: char (8), [signed | unsigned ] char, wchar_t (unicode), char16_t (UTF), char32_t. L'AB'

  • <cctype>:: isspace(), isalpha(), isdigit(), isalnum()

ISO C++ standard int types 

  • integer types: [signed | unsigned] short | int | long | long long

  • ::size_t x = sizeof(xxx) ; // implementation defined type

  • <limits>::numeric_limits // query arithmetic type properties

initialization gotchas – narrowing, default values, initialization lists

  • bool b2 {7};      // error: narrowing 

  • {} 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 

  • using PF = int(*)(double);  // pointer to function taking a double and returning an int

        void (*pf)(string)  = &DoSomething;  

        pf("blah");

        using F = void(*)(string);

  • beware: cannot dereference or increment void*

problems with passing arrays as arguments 

  • arrays cannot be copied, assigned, or passed by val

  • extern "C" int strlen(const char*);       // from <string.h> - the size of the array is lost to the called function. 

  • 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;   // must be 

  • 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 // no 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
  •  specify 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 <<

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()


 catch exception& best practices for handling exceptions 

  • 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 

  compiletime 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. 

extern // used for external linkage

        #ifdef __cplusplus

        extern "C" {}   // a linkage block

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 namepsaces, using directives

  • To ensure consistency (identical definition), place using 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

       

A class provides 6 default methods 

by default a class provides 6 things

        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


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 

  • 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 

        virtual ~Base() = 0;   // abstract base class definition (.cpp)


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

         MyClass o1 {};                     // default initialization


        MyClass o2 {   "aaa", 77 };          // memberwise initialization


        MyClass o3 { o2 };   // copy initialization

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 +=.  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:

        MyClass::operator int() const { return i; }      // a conversion operator resembles a constructor


// declaring an indexer

        const MyClass& operator[] (const int&) const;


// declaring a functor:

        int operator()(int);

        pair<int,int> operator()(int,int);


// the -> operator

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:

  • override and final are compiler hints

  • keywords virtual, override, final should only appear in .h files

  • override - The 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
  •         at least dynamic_cast is run-time checked - it supports polymorphic class hierarchies


limits of dynamic casting 

  • if (auto px = dynamic_cast<MyClass*>(py)) // dynamic_cast used an upcast - returns nullptrotherwise. 

  • 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

  • partial specialization   curbing code bloat - replicated code can cost megabytes of code space, so cut compile and link times dramatically

  • complete specialization - Specialize templates to optimize for important cases

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)

    

techniques to reduce compilation time when implementing templates :

  • Partial specialization       

  • 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.

 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 declerations of IO fascilities

        <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             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, aglorithms, allocators 
  • container adapters - eg stack, queue, deque - double ended queue
  • Iterators are a type of pointer used to separate algorithms and containers

  • 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

        hash<T> - functor

const iterators, reverse iterators 

        reverse iterators: rbegin, rend

        const iterators: cbegin, cend

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() - seperates 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().

 efficiently moving items from one container to another 

        copy(c1,make_move_iterator(back_inserter(c2)));    // move strings from c1 into c2

 getting an iterator from a reverse_iterator 

    Use base() to extract an iterator from a reverse_iterator

       

raw array vs array type 

  • T[N], array<T,N>

  • Use array where you need a sequence with a constexpr size

  • Prefer array over built-in arrays

 bitset<N> vs vector<bool>

  • bitset<N>, 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 vs tuple

  • pair<T,U>, tuple<T...>

  • When using pair, consider make_pair() for type deduction

  • When using tuple, consider 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: bind() , ref(), mem_fn(), function<T> 

  • <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 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

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() // 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

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

  • 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*> ,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_ptrs

  • 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

::hardware_concurrency() // reports the number of tasks that can simultaneously proceed with hardware support

        ::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 repetedly aquired 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()

 Signalling 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 

mechanisms of dealing with 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

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

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';

        prefxes: 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)


Posted on Wednesday, October 8, 2014 7:53 AM C++ | Back to top


Comments on this post: Standard Modern C++ in a Nutshell

# Dotnet Training in Chennai
Requesting Gravatar...
Well explained.Thank you..
Left by Leena deep on Jun 29, 2015 4:34 PM

# re: Standard Modern C++ in a Nutshell
Requesting Gravatar...
I have used this process and it worked well. - Morgan Exteriors
Left by Mike Abbott on Jan 05, 2017 6:21 PM

Your comment:
 (will show your gravatar)


Copyright © JoshReuben | Powered by: GeeksWithBlogs.net