Upload
nico-ludwig
View
149
Download
2
Embed Size (px)
DESCRIPTION
Check out these exercises: http://de.slideshare.net/nicolayludwig/3-cpp-procedural-programmingexercises - Procedural Programming - Predefined and User defined Functions - Declaration and Definition of Functions - Procedural and recursive Function Calling - Namespaces and separated Function Definitions - A Glimpse of Separated Compilation and Translation Units
Citation preview
1
Nico Ludwig (@ersatzteilchen)
(3) Basics of the C++ Programming Language
2
TOC
● (3) Basics of the C++ Programming Language
– Procedural Programming
– Predefined and User defined Functions
– Declaration and Definition of Functions
– Procedural and recursive Function Calling
– Namespaces and separated Function Definitions
– A Glimpse of Separated Compilation and Translation Units
● Sources:
– Bruce Eckel, Thinking in C++ Vol I
– Bjarne Stroustrup, The C++ Programming Language
3
Procedural Programming
● Imperative programming reaches its limits very soon.
– It is needed to repeat identical code parts all over in the program.
– If a repeated part contains a bug, this bug is present on all occurrences!
– We have to copy a code part to reuse it, or to share it with other developers.
– In general: Don't Repeat Yourself! DRY
– DRY in programming results in the the "Top-Down" approach.
● In engineering, complex problems are getting separated into smaller problems.
– In programming, problems are broken into procedures or functions.
– We call this the "Bottom-Up" approach, or functional decomposition.
● Procedural programming is all about the "Top-Down" and "Bottom-Up" notions.
– Top-Down: Programmers rework their code into functions to make it reusable.
– Bottom-Up: Programmers code new functions that solve subproblems.
● Procedural programming extends imperative programming with the concept of (reusable) functions.
4
Procedural Programming in C/C++
● In C/C++, procedural programming is implemented with functions.
● A function is a part of code that can be called independently.
– Formerly known as "sub program".
– In C/C++ there is no difference between procedures and functions.
– In functions, statements can be executed imperatively.
– In functions, further functions can be called as well.
● This code style is rather verb-oriented: "do this" and "call that" etc.
● Procedural code in C/C++:
– Program execution starts in the function main(), from where other functions are being called.
– In fact coding functions is the central activity of C/C++ programmers!
● What is a procedure?
5
Using predefined Functions in own Code
● The C/C++ standard library provides a plethora of functions for us.
– The C/C++ standards team already had their "Top-Down" specs on... :-)
– These functions allow reusing foreign code to solve own problems.
– E.g: std::pow(), std::qsort(), std::bsearch(), std::transform() etc.
– Some functions open the gate to the OS, like std::system().
● How to use functions from the standard library?
– #include the respective standard h-file, where the function declaration resides.
– (Optionally add a using directive for the namespace std.)
– Call the standard function within own code.
– (Mind to pass the lib file, where the functions' definition resides, to the linker.)
● "There is a problem with the usage of std::system()." What does this statement mean?● Well, std::system() accepts commands that are
highly platform-dependent in most cases.
6
Declaration and Definition of Functions
int GetCount (int x, int y)
{ // A block of code (function body).
}return 42;
Type of returned value or void. Function name Parameter list
Function bodyReturn value from function.
int GetCount (int x, int y = 0);Declaration:
Definition:
C++11 – trailing return typeauto GetCount(int x, int y) -> int {
return 42;}
int (*)(int, int)
The signature of GetCount.
Default argument
7
Features of Functions – Returning Values
● Functions can have a return type.
– When such a function is called, a value of type "return type" will be returned as a result of that function.
– This value can be stored (stuck into a variable) or otherwise consumed.
● Functions w/o return type are declared as returning void instead of a concrete type.
– Such functions don't have a result, but a persistent side effect in most cases.
● Functions, which only return values but have no side effects, are sometimes called pure functions.
● When a return statement in a function is executed (w/ or w/o returning a value) a function will be exited immediately!
// Store the returned value in a variable... (Mind that we have to write a pair of empty// parentheses, as GetDayOfMonth() accepts no parameters.):int day = GetDayOfMonth(); // …or use it in an expression:std::cout<<"The day is: "<<GetDayOfMonth()<<std::endl;// Finally the function can be called, ignoring the result: GetDayOfMonth();
// On calling ClearScreen(), the side effect happens, then ClearScreen() returns.ClearScreen(); // However, ClearScreen dos not return a value we could process!
// A function, which returns an int.int GetDayOfMonth() {
// pass}
// Clears the screen as side effect,// but doesn't return a value.void ClearScreen() {
// pass}
8
Features of Functions – Parameters
● The return type, the parameter types and the order of parameters make up the signature of a function.
● Function definition and declaration can have an optional list of parameters (params).
– A parameter is a function's variable storing the value of the passed argument.
– A set of functions can have the same name, but differ in the signature's parameter list.
● This handy feature is what we call "function overloading".
– The last items of the parameter list can be prepared with default arguments or build a variable argument list.
● On calling a function a list of arguments will be passed to satisfy the params.
– If a function requires no parameters, an empty pair of parentheses is written.
– Order and type of the arguments must match to the parameters respectively.
● By this matching, the correct (overloaded) function will be found.
● A matching (overloaded) function is resolved at compile time!
– Parameters having a default argument, need not to be "satisfied".
– Arguments are passed by value by default, so parameters contain copies of the arguments.
● It is not possible to overload return types!● Btw. selecting function overloads at run time can
also be implemented in C++ with so called "multiple dispatch".
● What is the difference between arguments and parameters?● A parameter is nothing but a local variable that
contains the value of the respective argument. Or: an argument is the initial value of the respective parameter.
● The passed argument expressions are evaluated in an unpredictable order!
9
Features of Functions – Calling Functions in a Nutshell
// No return type, no parameter list.void clearScreen() {
// pass}
// Call clearScreen() (mind the pair of empty parentheses), clearScreen() should// perform a side effect.clearScreen();
// No return type, awaits an int parameter.void checkAccount(int accountId) {
// pass}
// Call checkAccount(), pass 42 as argument.checkAccount(42);
// Returns int, awaits an int parameter. int getEntry(int entryID) {
// pass}
// Call getEntry(), pass 4 as argument, store the returned result in the variable theEntry.int theEntry = getEntry(4);
// Returns int, awaits two int parameters.int GetValue(int trackId, int index = 0) {
// pass}
// Call GetValue(), pass 7 (trackId) and then 7 (trackId) and 4 (index). Result ignored.GetValue(7); // The parameter index defaults to 0.GetValue(7, 4); // Both parameters are being satisfied.
10
Functions are called – there is no Code Replacement
● A common misunderstanding: function call leads to "code replacement".
– This is not what's going on!
● The functions A() and B() do always exist, their code is not "merged" somehow.
● Functions are rather procedurally called in C/C++.
● (Code replacement can be achieved with macros in C/C++.)
int A() {return 2 + 3;
}
void B() {int result = A();std::cout<<result<<std::endl;
}
void B() {int result = 2 + 3; // No!std::cout<<result<<std::endl;
}NO!B() calls A()
● There is no code replacement going on, it would lead to code bloat, and thus bigger executables after compilation.
11
Procedural Calling of Functions
● And again: functions can call other functions.
– When a function completes, execution returns to where the function was called from.
– "Top-level" functions call "lower-level" functions. So, there is a call hierarchy.
int main(int argc, char* argv[]) {double result = Square(3);
}
#include <cmath>
double Square(double arg) {return std::pow(arg, 2);
}
namespace std {double pow(double b, double exp) {
// calculate the powreturn value;
}}result = 9
(3)
(3, 2)
9
9
12
Recursive Calling of Functions
● Some problems can be described as smaller instances of the same problem.
– In maths such descriptions are called recursive descriptions.
– For example the factorial function (1 x 2 x 3 x 4 … x n):
● Factorial can be expressed as a recursive function in C/C++.
– In maths, recursion means that a equation is defined in terms of itself.
– In programming, recursion means that a function calls itself.
● How to write a recursive function?
– A subproblem of the whole problem must be used as argument for recursive calls.
– And it is needed to consider the base case, when the recursion terminates!
– (The progression of the recursive calls must tend to the base case.)
– If we fail to consider one of these cases, we may end with an infinite recursion!
n!={1; n=0
n((n−1)!); n>0 n∈N
● Where to use the "factorial" function?● E.g. the factorial is used to calculate the count of
permutations of a set of elements.● Infinite recursions run as long as all the stack
memory of the executing thread is exhausted.
13
Recursive Calling in Action
int main(int argc, char* argv[]) {int result = Factorial(3);
}
int Factorial(int n) {if (0 == n) {
return 1;} else {
return n * Factorial(n – 1);}
}
int Factorial(int n) {if (0 == n) {
return 1;} else {
return n * Factorial(n – 1);}
}
(3)(2)
62
int Factorial(int n) {if (0 == n) {
return 1;} else {
return n * Factorial(n – 1);}
}
(1)
1
int Factorial(int n) {if (0 == n) {
return 1;} else {
return n * Factorial(n – 1);}
}
(0)
1
0 == n
result = 6
● In the beginning, recursion is difficult to understand for most people. - They think about all the calls of a recursive function, in order to understand, if it is correct or not. They rather understand a function as a "machine" that processes inputs and returns outputs. → A better way to understand a function is that a function is a process. E.g. "process all documents and process all documents referred to in these documents".
14
Various Implementations of the Factorial Algorithm
int FactorialRecursive(int n) {if (0 == n) {
return 1;} else {
return n * FactorialRecursive(n – 1);}
}int FactorialIterative(int n) {
int result = 1;for (int i = n; i > 0; --i) {
result *= i;}return result;
}int FactorialNiceRecursive(int n) {
return (0 == n)? 1: n * FactorialNiceRecursive(n - 1);
}
n!={1; n=0
n((n−1)!); n>0 n∈N
15
Recursion and Recursive Functions in the Wild
● Useful to operate on recursive data. -> Data trees!
– E.g. a TreeView contains trees, that contain subtrees, that contain subtrees...
– Xml data is also a cascading tree of trees.
– Directory and file hierarchies.
– Network analyses, e.g. calculation of network efficiency and network load.
● Complex problems and puzzles can be solved easily with recursion.
– Maths: permutations of a set, numeric differentiation/integration
– Games: Towers of Hanoi, Sudoku, Chess puzzles etc.
● Definition of subproblems to be solved by multiple CPUs independently.
– The idea is to use CPU resources most efficiently -> Divide and conquer!
– Filters in graphics programming.
– Data analyses in networks and databases.
16
Organizing Functions in Namespaces
● So far all user defined functions we saw, were defined as global functions.
● Global functions may clash with equally named/typed other functions.
– The functions of the standard library reside in the namespace std to prevent this.
● Also do namespaces group free functions semantically.
– It turns out, that all free functions should reside in namespaces!
– Don't define global functions (save main()), use namespaces instead!
● C++ namespaces can be understood as directories and functions as files.
– In a file system directories do contain files.
– Files with the same name can reside in different directories. -> No clash!
● Let's examine how to define and use namespaces with the function Factorial()...
● What's a free function?● And what is its opposite?
17
// Function Factorial in the namespace Nico:namespace Nico {
int Factorial(int n) {return (0 == n)
? 1: n * Factorial(n - 1);
}}
Defining and using Namespaces in Code
// 1. Alternative: Qualify the function name// with the namespace's name:int main(int arc, char* argv[]) {
int result = Nico::Factorial(3);return EXIT_SUCCESS;
}
// 2. Alternative: Make all functions// in the namespace Nico global: with a// using directive:using namespace Nico;int main(int arc, char* argv[]) {
int result = Factorial(3);return EXIT_SUCCESS;
}
// 3. Alternative: Make only functions// named Nico::Factorial global: with// a using declaration:using Nico::Factorial;int main(int arc, char* argv[]) {
int result = Factorial(3);return EXIT_SUCCESS;
}
● Mind that namespaces allow us to have any other function named Factorial() residing in any other namespace than Nico; they will not clash with Nico::Factorial()!
18
Separated Function Definitions
● Related functions should be implemented in separate code files. - Why that?
– Each developer can manage just his functions, not interfering with others'.
– On modification, just the separately modified code files must be compiled.
– Very important: The implemented code can be hidden from its callers.
● Separate and collect function definitions. - What's to do?
– Collect related function definitions in own cpp/c-files (summarized "c-files", these are the code files).
– Create h-files with function declarations for the functions in the c-files respectively.
– These "matching" h- and c-files should have the same name.
● The file names should not contain whitespaces or special characters (like umlauts).
– As C/C++ convention, #include the h-files into the belonging cpp/c-files.
● Calling the separated functions. - What's to do?
– In our top-level code file, we've to #include the h-files declaring the functions we need.
– In the code we can call the functions, because the declarations are visible.
● So far the calling side of functions are discussed. But what's about the function definitions? Where do they reside? => Now we'll inspect translation units.
19
Documentation of Function Declarations
● The separation of function declaration and definition leads to information loss.
– In some sense this was the aim (callers should not see the definitions).
– But the mere function declaration is very opaque to us.
● How to improve the situation?
– Self description: Choose meaningful function and parameter names.
– Add documentation/usage comments to function declarations.
// numeric.hnamespace Nico {
/// Calculates the factorial./// @param arg – the positive value to calculate the factorial from./// @return – the factorial result.int Factorial(int arg);
}
20
Creation of the Translation Unit by the Preprocessor
// numeric.hnamespace Nico {
/// Calculates the factorial./// @param arg – the positive value/// to calculate the factorial from./// @return – the factorial result.int Factorial(int arg);
}
// Main.cpp#include "numeric.h"int main(int arc, char* argv[]) {
int result = Nico::Factorial(3);return EXIT_SUCCESS;
}
// Main.cpp tunamespace Nico {
int Factorial(int arg);}
int main(int arc, char* argv[]) {int result = Nico::Factorial(3);return EXIT_SUCCESS;
}// numeric.cpp#include "numeric.h"
namespace Nico {int Factorial(int n) {
return (0 == n)? 1: n * Factorial(n - 1);
}}
21
Separated Compilation and Linkage
● The separated c-files with function definitions need to be compiled also!
– In simple projects, we'll only have one code file (with the definition of main()).
– But in multi-file-projects we need to tell the compiler to compile all!
– Also the linker has to link all the resulting object-files (o-files) to an executable.
● How to negotiate with the compiler and linker about multiple files?
– We can, however, compile all c-files separately, e.g. on the console.
– We can write a make file that defines the build-tasks for compiler and linker.
– => In the end it is recommended to use an IDE to define this stuff in a project.
● Ok! What does a project within an IDE do for us?
– In the project we'll just collect all the h/c-files (and other files) we require.
– The IDE project will be automatically updated, when we add/remove h/c-files.
– Compiler and linker will be automatically prepared to deal with all the project's files.
● What other kinds of files could be managed within a IDE project?● E.g. resource files like icons.
22
Getting our Feet wet with IDE Projects and Functions
● Example – Decompose a problem:
– Often the function call std::system("pause") is used to wait for the user on console.
– Possibly this function call should be used by other programs as well.
– That solution is Windows only! It should be expressed by platform neutral means.
– Let us extract this into our own function the "Bottom-Up" way! → Call it Wait().
– We should put this new function into its own namespace!
– Implement with std::system("pause") and test it.
– Then implement it in a platform neutral fashion and test it again.
– => Decomposition complete!
● What does to function call std::system("pause") do (std::system() is declared in <cstdlib>)?
● Why is this solution Windows only?
23
Thank you!