Foundations of programming

Lecture eight - Modern C++

Work Plan

  1. Introduction, types of programming languages, pseudocode
  2. Variables, data types
  3. Simple instructions and tricks
  4. Pointers and arrays
  5. Functions and Procedures
  6. Input / Output
  7. Data Structures
  8. Modern C++
  9. Error Types /Debug
  10. Python
  11. Python II
  12. Python III
  13. Object Oriented / Functional programming
  14. Software development in XXI century
  15. Final exam / Test

Life beyond ANSI C

image

C++

C++ is a general purpose programming language that
* is a "superclass" of C
* supports data abstraction (represent the needed information in program without presenting the details)
* supports object-oriented programming
* supports generic programming.

History of C++

History of C++

C++ is standardized by an ISO working group.

Compilation

When you compile the program you can use the flag with specific value std:

-std=c++11

-std=c++14 

-std=c++17

Default language standard for compilation depends on the version of g++ compiler.

C++ philosophy

C++ philosophy

Modern C++ features

Namespace

A namespace provides a way for you to divide your program into different named "spaces".

Within each space you may re-use the names of variables, functions, etc.

Syntax:

namespace Q {
    namespace V { ...
    }
}

Here we define namespace Q and within it namespace V. You can define variables, function and classes inside the namespaces.

Most important namespace is std it encapsulates all the classes and functions defined by the STL library.

Using

Using-declarations introduces a name that is defined elsewhere into the declarative region where this using-declaration appears.

In other words you need to declare using the namespace to be able to access its elements.

C++ allows you to import namespace via the using statement keywords.

using namespace std;

Ordinarily you can also change names in a namespace by prefixing them with the name of their namespace and two colons (::). For example, to declare a variable as a list you would write:

std::list<int> intList;    

C++

Polymorphism

Polymorphism

Polymorphism means one thing in many form.

Polymorphism enables one common interface for many implementations, and for objects to act differently under different circumstances

Single operator behave different for different data type. Example: addition operator use to add integer and concatenate for string.

Static polymorphism

Static polymorphism (Compile-time Polymorphism) decides which method to execute during compile time.

In static polymorphism many functions have some differences as number, type, and sequence of arguments.

Example: Function declaration the various types of parameters are specified, and therefore the function can be bound to calls at compile time.

In C++ static polymorphism is obtained through function overloading and operator overloading.

Dynamic polymorphism

Dynamic polymorphism where an entity can modify its form depending on the situation.

A function is dynamic polymorphism when one function can have more then one form and the call of various form can be resolved at run time dynamically or at the time of execution.

In C++ static polymorphism is obtained through virtual function (we will not discuss virtual functions during this class).

Dynamic polymorphism

image

Operator overloading

An overloaded declaration is a declaration that is declared with the same name as a previously declared declaration in the same scope, except that both declarations have different arguments and different definition (implementation).

When you call an overloaded function or operator, the compiler determines the most appropriate definition to use.

Overloaded operators are functions with special names the keyword operator followed by the symbol for the operator being defined.

Like any other function, an overloaded operator has a return type and a parameter list.

Operator overloading

You can redefine or overload most of the built-in operators available in C++.

Overloaded operators are functions with special function names:

operator X ( )
{
    ....
}

Where X is any of the 38 operators:

+ - * / % ˆ & | ~ ! = < > += -= *= /= %= ˆ= &= |= << >> >>= <<= == != <= >= && || ++ -- , ->* -> ( ) [ ]

Overloading an operator does not change the precedence of calculations involving the operator, nor does it change the number of operands that the operator uses.

Operator overloading

Example:

struct Student {   
    int nr_index;
    float average; 
};
bool operator==(const Student &a, const Student &b) {
    return a.nr_index==b.nr_index;
}
bool operator< ( Student const &a, Student const &b) const {
    return a.average < b.average;
}

Operator overloading

We can define an overloading operator as a member of a struct (class), then it takes one argument, or outside - nonmember, then it will take two arguments.

struct Distance {

  int meters;             
  int cm;  

    bool operator<(const MyStruct& rhs) const
    {
        if (meters < rhs.meters)            
        return true;
        else
        if (cm < rhs.cm)            
        return true;
    }
}

Overloading comparison operator is necessary to be able to sort() structures.

Overloading stream

The stream insertion and stream extraction operators also can be overloaded to perform input and output for user-defined types like an object.

struct Distance {
    int meters;             
    int cm;  
};

std::ostream & operator <<( std::ostream & output, const Distance & D ) { 
    output << "M : " << D.meters << " Cm : " << D.cm;
    return output;            
}        

Operator Overloading in C++

The Three Basic Rules of Operator Overloading in C++:

1. Whenever the meaning of an operator is not obviously clear and undisputed, it should not be overloaded. Instead, provide a function with a well-chosen name.
2. Always stick to the operator’s well-known semantics.
3. Always provide all out of a set of related operations (if your type supports a + b, users will expect to be able to call a += b, too.)

Important facts

Operator overloading allows you to redefine the way operator works for user-defined types only (objects, structures). It cannot be used for built-in types (int, float, char etc.).

Two operators = and & (address) are already overloaded by default in C++. For example: To copy objects of same class, you can directly use = operator. You do not need to create an operator function.

Operator overloading cannot change the precedence and associativity of operators.

There are 4 operators that cannot be overloaded in C++. They are :: (scope resolution), . (member selection), .* (member selection through pointer to function) and ?: (ternary operator).

Generics

Generic programming

Generic programming means that you are not writing source code that is compiled as-is but that you write "templates" of source codes that the compiler in the process of compilation transforms into source codes.

For example you define a type List without saying which type is collected in the list.

So called generic types are elements of almost every modern object oriented programming language.

C++ uses templates to enable generic programming techniques.

Generic programming

In the context of C++ generic elements of programs are evaluated at compile time.

Generics are used to generate short, simple code and to avoid duplication of code,

With templates, programmers can define a family of functions or classes that can perform operations on different types of data.

Templates are widely used to implement the Standard Template Library (STL).

Function Template

A function template is a function which contains generic code to operate on different types of data.

This enables a programmer to write functions without having to specify the exact type of parameters.

To define template we use keyword template followed by generic type names.

template<class Type, ...>
return–type function–name(Type arg1, ...)
{
    //Body of function template
}

Example

#include <iostream>

using namespace std;

template<class Type>
void summ(Type x, Type y)
{
    cout<<“Sum is: “<<x+y<<endl;
}


int main()
{
    int a = 10, b = 20;
    summ(a, b);
    float p = 1.5, q = 2.4;
    summ(p, q);
    return 0;
}

Output:

Sum is: 30
Sum is: 3.9

In the above program compiler generates two copies of the above function template. One for int type arguments and the other for float type arguments.

Using templates

Every template data type (or generic data type) should be used as types in the function template definition as type of parameters.

Wrong:

template<class Type>
void summ(int x, int y) 

Use all template data types.

Wrong:

template<class Type1, class Type2>
void summ(Type1 x, int y) //Error since Type2 is not used

Using templates

...

A template parameter pack (...) is a template parameter that accepts zero or more template arguments.

Example:

template<class ... Types> struct Tuple {};

Tuple<> t0;           // Types contains no arguments
Tuple<int> t1;        // Types contains one argument: int
Tuple<int, float> t2; // Types contains two arguments: int and float

For functions, a function with three dots means, that you can pass any number of variables as arguments.

void func(unsigned int n_args, int arg, ...)
{
    for(unsigned int i = 0; i < n_args; ++i)
        cout << *((int*)&arg + i) << ' ';
}    

Example ...

template<typename T>
T adder(T v) {
  return v;
}

template<typename T, typename... Args>
T adder(T first, Args... args) {
  return first + adder(args...);
}

And here are a couple of ways we could call it:

long sum = adder(1, 2, 3, 8, 7);

std::string s1 = "x", s2 = "aa", s3 = "bb", s4 = "yy";
std::string ssum = adder(s1, s2, s3, s4);

Templates

Templates are generic because the compiler translates the template into actual code.

To instantiate a template, compilers substitute specific arguments for a template's parameters to generate a concrete function or class instance.

You can use templates to perform operations during compilation.

Lambdas

Lambda expressions

Sometimes called anonymous functions.

Example:

[](int x, int y) -> int { return x + y; }

Unnamed function that take two integers and returns its sum.

Example:

// generic lambda, operator() is a template with two parameters
auto glambda = [](auto a, auto&& b) { return a < b; };
bool b = glambda(3, 3.14); // ok

Lambda expressions

Lambda expression are defined with [] that are followed by the list of arguments and -> on the right side you put the returning type and the body of the function.

[]()->int { return 123; };

is equivalent to :

int funkcja()
{
    return 123;
}

Real world application

To create function "in place" in order to make code more clear and easier to maintain.

Example:

std::sort( vector.begin(), vector.end(),[]( const int & a, const int & b )->bool { return a > b; } );

Or

std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });

Exceptions

Exception handling

Exceptions provide a way to react to exceptional circumstances (like runtime errors) in programs by transferring control to special functions called handlers.

we say the exceptio is thrown and afterwards caught by the exception handler.

The exception causes the current scope to be exited, and also each outer scope (propagation) until a suitable handler is found.

Try...Catch

The exception-causing code is placed inside a try block. The exceptions are handled in separate catch blocks (the handlers); each try block can have multiple exception handlers.

If no exception is thrown, the code continues normally and all handlers are ignored.

Try...Catch

You define try...catch block with keywords try and catch

try
{
    ....
}
catch ()
{
    .....
}

The C++ Standard library provides a declaration of objects to be thrown as exceptions. It is called std::exception and is defined in the <exception> header.

Throw

Exceptions can be thrown anywhere within a code block using throw statement.

throw param;

Param can be an object of any type.

throw 0;
throw std::out_of_range ("description");

You can specify what type of exception you want to catch in the catch statement (they have to match type given in throw).

You can use ... to catch any exception

catch(...)

Try...Catch

#include <stdexcept>

int main() {
    try {
        std::vector<int> vec{3, 4, 3, 1};
        int i = vec.at(4); // Throws an exception, std::out_of_range (indexing for vec is from 0-3 not 1-4)
    }

    // An exception handler, catches std::out_of_range, which is thrown by vec.at(4)
    catch (std::out_of_range &e) {
        std::cerr << "Accessing a non-existent element: " << e.what() << '\n';
    }

    // To catch any other standard library exceptions (they derive from std::exception)
    catch (std::exception &e) {
        std::cerr << "Exception thrown: " << e.what() << '\n';
    }

    // Catch any unrecognised exceptions (i.e. those which don't derive from std::exception)
    catch (...) {
        std::cerr << "Some fatal error\n";
    }
}

Why use exceptions?

Functions/Methods can handle any exceptions they choose

Separation of Error Handling code from Normal Code.

You can easily report error to the program user.

Exceptions have efficient implementation in modern C++.

Using exceptions for error handling makes your code simpler, cleaner, and less likely to miss errors.

C++ 17

C++17

Newest, just published (december 2017), standard for C++.

Draft version published in March 2017.

Add new features to the language:
* Paraller version of algorithms in STL.
* improved auto deduction
* string_view as a preferred interface vocabulary type for APIs that need to view a string without wanting to take ownership or to modify it.

Official list of changes :

Filesystem

A file system library based on boost::filesystem.

The library is located in the <filesystem> header. It uses namespace std::filesystem.

Three core parts:
The path object
directory_entry
Directory iterators
Plus many supportive functions:
getting information about the path
files manipulation: copy, move, create, symlinks
last write time
permissions
space/filesize

std::any

Datatype to store data of any type, "python - like".

A better way to handle any type and replace void*.

"The discriminated type may contain values of different types but does not attempt conversion between them".

auto a = std::any(12);
a = std::string("hello world");
a = 10.0f;

When you want to read a value you have to perform a proper cast:

auto a = std::any(12);
std::cout << std::any_cast<int>(a) << '\n';    

std::optional

Optional data type means allow a variable to bo empty or store a value.

Example:

std::optional<std::string> ostr = GetUserResponse();

if (ostr)
    ProcessResponse(*ostr);
else
    Report("please enter a valid value");

References

https://en.wikipedia.org/wiki/C%2B%2B

http://www.bfilipek.com/2017/09/cpp17-details-utils.html

https://isocpp.org/files/papers/p0636r0.html

http://www.bfilipek.com/2017/08/cpp17-details-filesystem.html

http://en.cppreference.com/w/cpp/language/namespace

http://r4r.co.in/cpp/cpp_oops_tutorial/Polymorphism.shtml

https://codingsec.net/2016/12/generic-programming-c/