8. Technicalities: Functions, etc.
8.1 Technicalities
 
                    
                      When you start programming, your programming language
                      is a foreign language for which you need to look at
                      “grammar and vocabulary”.
                      Most design and programming concepts are universal,
                      and many such concepts are widely supported by popular
                      programming languages.
                      That means that the fundamental ideas and techniques
                      we learn in a good programming course carry
                      over from language to language.
                      The language technicalities, however, are specific to a
                      given language. Fortunately, programming languages do not
                      develop in a vacuum, so much of what you learn here will
                      have reasonably obvious counterparts in other languages.
                      
                      In particular, C++ belongs to a group of languages
                      that also includes C, Java, and C#,
                      so quite a few technicalities are shared with those
                      languages.
                    
8.2 Declarations and definitions
 
                    A declaration is a statement that introduces a name into a scope:
- Specifying a type for what is named (e.g., a variable or a function)
- Optionally, specifying an initializer (e.g., an initializer value or a function body)
Example:
                        
                          int a = 7; // an int variable
                          const double cd = 8.7; // a double-precision floating-point constant
                          double sqrt(double); // a function taking a double argument
                                               // and returning a double result
                          vector<Token> v; // a vector-of-Tokens variable
                        
                      
                    
                    
                      Before a name can be used in a C++ program,
                      it must be declared. 
                      Consider:
                      
                        
                          int main()
                          {
                              cout << f(i) << '\n';
                          }
                        
                      
                      The compiler will give at least three
                        “undeclared identifier” errors for this:
                        cout, f, and i
                         are not declared anywhere in this program fragment.
                    
                    After Fixing:
                        
                          #include "std_lib_facilities.h" // we find the declaration of cout in here
                          int f(int); // declaration of f
                          int main()
                          {
                            int i = 7; // declaration of i
                            cout << f(i) << '\n';
                          }
                        
                      
                      This will compile because every name has been declared,
                         but it will not link because we have not defined
                         f(); that is, nowhere have we specified
                         what f() actually does.
                      
                    
                    
                      A definition specifies exactly what a name refers
                      to. In particular, a definition of a variable sets
                      aside memory for that variable.
                      Example:
                      
                        
                          int x = 7; // definition
                          double sqrt(double d) { /* . . . */ } // definition
                        
                      
                    
                    8.2.1 Kinds of declarations
                          Following are the kind of entities that a programmer
                          can define in C++:
                          
- Variables
- Constants
- Functions
- Namespaces
- Types (classes and enumerations)
- Templates
8.2.2 Variable and constant declarations
 
                        
                          The declaration of a variable or a constant specifies
                          a name, a type, and optionally an initializer:
                          
                            
                              int a; // no initializer
                              double d = 7; // initializer using the = syntax
                              vector<int> vi(10); // initializer using the ( ) syntax
                              vector<int> vi2 {1,2,3,4}; // initializer using the { } syntax
                            
                          
                        
                        
                          Constants have the same declaration syntax as
                          variables. They differ in having const
                           as part of their type and requiring an initializer:
                           
                           
                             
                                const int x = 7; // initializer using the = syntax
                                const int x2 {9}; // initializer using the {} syntax
                             
                           
                        
                    8.2.3 Default initialization
                            
                              vector<string> v;
                              string s;
                              while (cin>>s) v.push_back(s);
                            
                          
                        
                        
                          Here, string and vector are
                          defined so that variables of those types are
                          initialized with a default value whenever we don’t
                          supply one explicitly.
                          Thus, v is empty (it has no elements)
                          and s is the empty string
                          ("") before we reach the loop.
                           The mechanism for guaranteeing
                           default initialization is called a
                           default constructor.
                        
Unfortunately, the language doesn’t allow us to make such guarantees for built-in types.
8.3 Header files
 
                    
                      As programs grow larger and larger
                      (and include more files), it becomes increasingly tedious
                      to have to forward declare every function you want to
                      use that lives in a different file. 
                      Wouldn’t it be nice if you could put all your
                      declarations in one place?
                    
                      Header files usually have a .h
                      extension, but you will sometimes see them with a
                      .hpp extension or no extension at all.
                      
                      The purpose of a header file is to hold declarations
                      for other files to use.
                    
Drill
                        Separate out the tokenizing code from the rest of your
                        calculator program. Put your declarations in
                        token.h and your definitions to
                        token.cpp. Get this version to compile and
                        link.
                    
8.4 Scope
 
                    
                      Scope is a region of program text
                      with the main purpose to keep  the extent of a
                      program code within which the variable can be accessed.
                      
                      Kinds of scopes used to control where names
                      can be used are:
                      
- Global scope: the area of text outside any other scope
- Namespace scope: a named scope nested in the global scope or in another namespace
- Class scope: the area of text within a class
- Local scope:
                          between { . . . }braces of a block or in a function argument list
- Statement scope:
                           e.g., in a for-statement
8.5 Function call and return
 
                         
                    
                      Functions are the way we represent actions and
                      computations.
                      In a programming language, a function is a type of
                      procedure or routine. Functions allow us to reuse code
                      instead of rewriting it.
                      They also help organize the code.
                    
8.5.1 Declaring arguments and return type
 
                        
                          A function can be declared in following way:
                          return_type function_name( parameter list );
                          
                          A definition contains the function body
                          (the statements to be executed by a call),
                          whereas a declaration that isn’t a definition
                          just has a semicolon.
                          Formal arguments are often called parameters.
                          
- If you don’t want a function to take arguments, just leave out the formal arguments.
- 
                                If you don’t want to return a value
                                from a function, give voidas its return type.
8.5.2 Returning a value
                          A return type  specifies
                          the type of the data value being returned by the
                          written function.
                          Also, function may or may not return a value.
                          The return type is void if the function
                          does not return a value.
                          
                            
                              void print_until_s(vector<string> v, string quit)
                              {
                                for(int s : v) {
                                  if (s==quit) return;
                                  cout << s << '\n';
                                }
                              }
                            
                          
                        
                        
                          In code shown in 8.5.1
                          , the return value is an integer value
                          c, which is int.
                        
8.5.3 Pass-by-value
                          The simplest way of passing an argument to a
                          function is to give the function a copy of the value
                          you use as the argument. An argument of a function
                          f() is a local variable in
                          f() that’s initialized each time
                          f() is called.
                          Example:
                          
                            
                              // pass-by-value (give the function a copy of the value passed)
                              int f(int x)
                              {
                                x = x+1; // give the local x a new value
                                return x;
                              }
                              int main()
                              {
                                int xx = 0;
                                cout << f(xx) << '\n'; // write: 1
                                cout << xx << '\n'; // write: 0; f() doesn’t change xx
                                int yy = 7;
                                cout << f(yy) << '\n'; // write: 8
                                cout << yy << '\n'; // write: 7; f() doesn’t change yy
                              }
                            
                          
                        
                        
                          Graphical Representation:
                         
                        
8.5.4 Pass-by-const-reference
                          If a value is large, such as an image
                          (often, several million bits),
                          a large table of values (say, thousands of
                          integers), or a long string
                          (say, hundreds of characters)?
                          
Then, Pass-by-value
                           can be costly.
                           Example:
                           
                             
                                void print(const vector<double>& v) // pass-by-const-reference
                                {
                                  cout << "{ ";
                                  for (int i = 0; i<v.size(); ++i) {
                                      cout << v[i];
                                      if (i!=v.size()–1) cout << ", ";
                                  }
                                  cout << " }\n";
                                }
                                void f(int x)
                                {
                                  vector<double> vd1(10); // small vector
                                  vector<double> vd2(1000000); // large vector
                                  vector<double> vd3(x); // vector of some unknown size
                                  // . . . fill vd1, vd2, vd3 with values . . .
                                  print(vd1);
                                  print(vd2);
                                  print(vd3);
                                }
                              
                            
                        
                        
                          The above code shows a way of giving our
                          print() function “the address” of the
                          vector to print() rather than the
                          copy of the vector. Such an “address” is
                          called a reference.
                          The & means “reference” and
                          the const is there to stop print()
                           modifying its argument by accident. Apart from
                           the change to the argument declaration, all is the
                           same as before; the only change is that
                           instead of operating on a copy, print()
                            now refers back to the argument through the
                            reference.
                        
                          Graphical Representation:
                           
                          
8.5.5 Pass-by-reference
                          Passing by reference means the called functions'
                          parameter will be the same as the callers'
                          passed argument
                          (not the value, but the identity -
                          the variable itself).
                          A reference can be a convenient
                          shorthand for some object, is what makes them
                          useful as arguments.
                          Example:
                          
                            
                              // pass-by-reference (let the function refer back to the variable passed)
                              int f(int& x)
                              {
                                x = x+1;
                                return x;
                              }
                              int main()
                              {
                                int xx = 0;
                                cout << f(xx) << '\n'; // write: 1
                                cout << xx << '\n'; // write: 1; f() changed the value of xx
                                int yy = 7;
                                cout << f(yy) << '\n'; // write: 8
                                cout << yy << '\n'; // write: 8; f() changed the value of yy
                              }
                            
                          
                        
                        
                          Graphical Representation:
                           
                          
Application in Sorting (Swap function):
                            
                              void swap(double& d1, double& d2)
                              {
                                double temp = d1; // copy d1’s value to temp
                                d1 = d2; // copy d2’s value to d1
                                d2 = temp; // copy d1’s old value to d2
                              }
                              int main()
                              {
                                double x = 1;
                                double y = 2;
                                cout << "x == " << x << " y== " << y << '\n'; // write: x==1 y==2
                                swap(x,y);
                                cout << "x == " << x << " y== " << y << '\n'; // write: x==2 y==1
                              }
                            
                          
                        
                    8.5.6 Pass-by-value vs. pass-by-reference
 
                        Our rule of thumb is:
- Use pass-by-value to pass very small objects.
- Use pass-by-const-reference to pass large objects that you don’t need to modify.
- Return a result rather than modifying an object through a reference argument.
- Use pass-by-reference only when you have to.
8.5.7 Argument checking and conversion
When an argument is passed during a call, initialization of the function’s formal argument with the actual argument takes place.
                            
                              void f(double x);
                              void g(int y)
                              {
                                f(y);
                                double x = y; // initialize x with y
                              }
                            
                          
                        
                        
                          There was a need to convert an
                          int into a double.
                          This happens in the call of f()
                          and also in f() where double
                           value is stored in x.
                          In conversions,
                          if a double value has to be truncated
                          into an int mention it explicitly.
                          
                            
                              void ggg(double x)
                              {
                                int x1 = x; // truncate x
                                int x2 = int(x);
                                int x3 = static_cast<int>(x); // very explicit conversion
                                ff(x1);
                                ff(x2);
                                ff(x3);
                                ff(x); // truncate x
                                ff(int(x));
                                ff(static_cast<int>(x)); // very explicit conversion
                              }
                            
                          
                        
                    8.5.8 Function call implementation
                          Lets say we have functions expression(), term(), 
                            primary().
                          A data structure is set aside which contains
                          copy of all parameters and local variables as soon
                          as a function is called.
                          A data structure called function activation
                            record which maintains information like the
                            function to which the caller needs to
                            return and return value is maintained.
                          Now, expression() is called first:
                           
                          
                        
                          expression() calls term():
                           
                          
                        
                          term() calls primary():
                           
                          
                        
                          And then primary() calls
                          expression():
                           
                          
                        
                          So this becomes a recursive function.
                          Each time when a function is called, the
                          stack of activation records (stack) grows
                          by one record.
                          When the function returns, the record is no
                          longer in use.
                          That means the stack reverts.
                          It works as "Last in, first out".
                           
                          
                           
                          
8.5.9 constexpr functions
                          A constexpr function is a non-constructor
                          function declared with a constexpr specifier.
                          It is a function that can be invoked within a
                          constant expression.
                          Conditions to be satisfied by a
                          constexpr function are:
                          
- It cannot be virtual.
- Return type must be literal type.
- Also each of its parameters must be literal type as well.
- In a constant expression, each constructor call and implicit conversion is valid while initializing the return value.
- Function body is = deleteor= default
- If not function body must contain null statements, static_assert declarations, typedef declarations, using declarations, using directives, one return statement
8.6 Order of evaluation
 
                    
                      The evaluation/execution of a program proceeds
                      through statements according to the language rules.
                      The order of evaluation is as follows:
                      
- Variable is constructed when the "thread of execution" reaches the definition of a variable
- Object is initialized by setting aside memory for the object
- Variable destroyed as it goes out of scope
- Object referred by the variable is removed
- Memory is free for the compiler to be used for something exclusively
                        
                          string program_name = "silly";
                          vector<string> v; // v and program_name are global, they live until program terminates
                          void f()
                          {
                              string s; // s is local to f
                              while (cin>>s && s!="quit") {
                                  string stripped; // stripped is local to the loop
                                  string not_letters;
                                  for (int i=0; i<s.size(); ++i) // i has statement scope
                                      if (isalpha(s[i]))
                                          stripped += s[i];
                                      else
                                          not_letters += s[i];
                                  v.push_back(stripped);
                                  // . . .
                              }
                              // . . .
                          }
                        
                      
                    
                    8.6.1 Expression evaluation
If the value of a variable in an expression is changed, never read or write it twice in that same expression.
                            
                              v[i] = ++i; // don’t: undefined order of evaluation
                              v[++i] = i; // don’t: undefined order of evaluation
                              int x = ++i + ++i; // don’t: undefined order of evaluation
                              cout << ++i << ' ' << i << '\n'; // don’t: undefined order of evaluation
                              f(++i,++i); // don’t: undefined order of evaluation
                            
                          
                        
                    8.6.2 Global initialization
Global variables in a single translation unit are initialized in the order in which they appear.
                            
                              int x1 = 1;
                              int y1 = x1+2; // y1 becomes 3
                            
                          
                          This initialization logically takes place
                          “before the code in main() is executed.
                        
                        
                            
                              const Date& default_date()
                              {
                                static const Date dd(1970,1,1); // initialize dd first time we get here
                                return dd;
                              }
                            
                          
                          The static local variable is initialized
                          (constructed) only the first time its function is
                          called.
                        
                    8.7 Namespaces
Classes are used to organized functions, data and types into a type. A function and a class both do two things:
- Allow to define a number of "entities" (without worrying about name clashes).
- Give a name to refer to what is defined.
                      A namespace is a language
                      mechanism for groupings of declarations.
                      It is used to organize classes, functions,
                      data and types into an identifiable and
                      named part of the program without defining a type.
                    
                        
                          namespace Graph_lib {
                            struct Color { /* . . . */ };
                            struct Shape { /* . . . */ };
                            struct Line : Shape { /* . . . */ };
                            struct Function : Shape { /* . . . */ };
                            struct Text : Shape { /* . . . */ };
                            // . . .
                            int gui_main() { /* . . . */ }
                          }
                        
                      
                    
                    8.7.1 using declarations and using directives
Writing fully qualified names can be tedious.
                            
                              #include<string> // get the string library
                              #include<iostream> // get the iostream library
                              int main()
                              {
                                std::string name;
                                std::cout << "Please enter your first name\n";
                                std::cin >> name;
                                std::cout << "Hello, " << name << '\n';
                              }
                            
                          
                        
                        The above code can be reduced to:
                            
                              #include<string> // get the string library
                              #include<iostream> // get the iostream library
                              using namespace std; // make names from std directly accessible
                              int main()
                              {
                                string name;
                                cout << "Please enter your first name\n";
                                cin >> name;
                                cout << "Hello, " << name << '\n';
                              }
                            
                          
                        
                        
                          It can be further reduced to by placing a
                          using derivative for std:
                          
                            
                              #include "std_lib_facilities.h"
                              int main()
                              {
                                string name;
                                cout << "Please enter your first name\n";
                                cin >> name;
                                cout << "Hello, " << name << '\n';
                              }
                            
                          
                        
                    Test Yourself!
 
                        - What is the difference between a function declaration and a function definition?
- the declaration includes the function body, while the definition just gives the function's return and parameter types
- the declaration just gives the function's return and parameter types, while the definition includes the function body
- What are header files used for?
- To head off disaster.
- To put a nice header at the top of your code if you print it.
- To provide declarations needed for multiple source code files.
- How does indentation help in a C++ program?
- it helps the compiler do code optimizations
- it makes the structure of the code clearer to a human reader
- it is necessary to compile the code correctly, just as in Python
- If we need to alter the original of a variable we are passing to a function, we should...
- pass by constant reference
- pass by reference
- pass by value
- The function declaration consists of …
- function name, return type and parameter list
- function body and parameter list
- function name and parameter list
- Which of the following is not a type of scope?
- Namespace Scope
- Declaration Scope
- Class Scope
- Global Scope
- Passing by reference means …
- parameter will be the same as the callers' passed argument (not the value, but the identity - the variable itself)
- the called function's parameter will be a copy of the callers' passed argument
- Passing by value means …
- parameter will be the same as the callers' passed argument (not the value, but the identity - the variable itself)
- the called function's parameter will be a copy of the callers' passed argument
- Function definition consists of …
- function name and body
- function body and return type
- function name, parameters, return value type, and body
- What is the return type here: int myMethod(int count, double value) { return 4; }
- MyMethod
- int
- 4
- double
- count
- If a variable is declared inside a function, what kind of variable is this?
- global variable
- local variable
- class variable
- If we have a function int square (int n) , are we able to send it a different variable in the main program or does it have to be n. For example, square (x)
- No
- Yes
- Which of the following is a valid function call (assuming the function exists)?
- f();
- f;
- f x, y;
- int f();
- Identify the correct statement
- None of the mentioned
- A namespace is used to mark the beginning of the program
- A namespace is used to separate a class from objects
- A namespace is used to group classes, objects and functions
- What is the scope of the variable declared in a user defined function?
- none of the mentioned
- whole program
- the main function
- only inside the {} block in which it is declared
- If your function does not return any value, which of the following keywords should be used as a return type?
- double
- int
- void
- float
- unsigned short
- Which of the following functions should be defined as void?
- Return a sales commission, given the sales amount and the commission rate
- Print the calendar for a month, given the month and year
- Return a bool value indicating whether a number is even
- Return a square root for a number
- Which of the following should be declared as a void function?
- a function that prints integers from 1 to 100
- a function that checks whether current second is an integer from 1 to 100
- a function that converts an uppercase letter to lowercase
- a function that returns a random integer from 1 to 100
Answers
1. b; 2. c; 3. b; 4. b; 5. a; 6. b; 7. a; 8. b; 9. c; 10. b; 11. b; 12. b; 13. a; 14. d; 15. d; 16. c; 17. b; 18. a;
Drill
