400 likes | 656 Views
Default arguments Overloading Inlining Call-by-reference Scope of variables, static, extern namespace new and delete assert. Functions in C++. 159.234. Functions. we can call this function in two different ways: j = power(5,3); // 5 cubed j = power(5); // 5 squared
E N D
Default arguments Overloading Inlining Call-by-reference Scope of variables, static, extern namespace new and delete assert Functions in C++
159.234 Functions we can call this function in two different ways: j = power(5,3); // 5 cubed j = power(5); // 5 squared only trailing arguments may be defaulted more than one argument can be defaulted: void fct(int j=4, int k=5, int m=7); In C++ the use of void is optional in a function that has no arguments: intfct() { return global*2; } Default arguments: // k has a default value of 2 int power(int n, int k = 2) { if (k == 2) { return n*n; } return power(n,k-1)*n; } C++
159.234 Overloading C++ allows functions with arguments of differing types, to have the same name. We call this function overloading. float max(float x, float y) { //is OK return (x>y)?x:y; } intmax(int m, int n) { return (m>n)?m:n; } What about the max of two floats? max is already in use – float fmax(float x, float y) { return (x>y)?x:y; } C++
159.234 Functions One function can have more arguments than another, or the types of the arguments can be different. The compiler chooses which function to use by matching the arguments. 1. an exact match 2. a match by promoting float -> double, short -> long, etc. 3. a match by converting float -> int An overloaded function must have parameters that differ in some way. Differing only by return type is not allowed. C++
159.234 Inlining: But this makes for messy code, especially if k is a computed expression. The keyword - inline - asks the compiler to generate inline code – as though we had written out the code each time. inlineint cube (int j) { return j*j*j; } inline functions cannot be recursive, and are normally very short. int cube(int j) { return j*j*j; } The drawback with this code is its inefficiency. The time required to call the function may be the same as the calculation. If the function is called many times, the program slows down. The alternative is to ignore functions. Write out the code each time it is used: i = k*k*k; C++
159.234 Call-by-reference If a C function wants to alter an argument, then a pointer to the variable must be used int main() { int j = 4; change_it(&j); ... } void change_it(int *ip) { (*ip) = 6; } This works well for simple types. When pointers themselves are passed as arguments, it can be quite confusing - with pointers to pointers. **p or even ***p can take some time for a human to unravel. C++
159.234 In C++, functions can use call-by-reference. int main() { inti,j; ... swap(i,j); ... } void swap(int &p, int &q) { int temp; if (p > q) { temp = p; p = q; q = temp; } } p and q are called ‘reference parameters’. They refer back to the original variables - so the function can alter its parameters. We can create ‘reference variables’ similarly: int main() { int m; int &p = m; p is now just another name for the variable m. C++
159.234 Scope: int main() { ... } int j = 4; void fct() { cout << j; } j is a global variable, but main() cannot access it. Our programs so far have been small in size and have fitted into one file. Variables have been declared as either global - at the top of the file or local - at the beginning of a function. This is not the whole story: Variables: that are declared outside functions are global. They must be declared before they are used. C++
Scope The portion of the program where an identifier has meaning is known as its scope. In C++, local variables can be declared anywhere within a function. (statements and declarations can be mixed up in order, but a variable must be declared before it is used) When the enclosing {} ends, the variable is destroyed (from the stack part of the memory) if (x > 0){ y = x; int z = x*x + x; w = y + z*z; }
Scope The scope resolution operator ::is used to access a name that has external scope and has been hidden by local scope. inti = 50; //global variable int main() { inti = 100; // this one is not seen for( inti = 0; i < 5; ++i){ cout <<"\nLocali is "<< i << " "; cout <<"Global i is "<< ( ::i ); } } This will print i = 0, 1, 2, etc for local and 50 for the global
159.234 Static Local Variables char *fct() { static char s[16]; char *p p = &s[0]; ... return p; } s retains its value from one call of fct() to another. Static local variables are similar to globals – their default value is 0 and they last until the program ends - but they are only accessible within the block that they are declared. Local variables can be declared anywhere within a function. When the enclosing {} ends, the variable is destroyed. if (x > 0) { y = x; int z = x*x + x; w = y + z*z; } Static variables: Static local variables are not destroyed when the enclosing block ends. C++
159.234 Local Variables They are useful when returning a pointer to an area of memory: char *fct() { char s[16], *p; p = s; ... return p; // ! } is a mistake most people make in their lives. The returned pointer points to an area of memory that no longer contains anything useful! C++
159.234 Global Variable to be Used in Multiple Files With large programs it is normal to split the code over several files. When a global variable is needed by code in more than one file we must: define the variable in one file e.g. int j; declare the variable as extern in all other files: extern int j; It is normal to put the extern declarations in a header file, and include this header file in all code files. The compiler does not complain if it sees: extern int j; int j; C++
159.234 STATIC FUNCTIONS mylib.c void fct1(void); //available to others static void printit(void); //only for me void fct1() { ... printit(); ... } static void printit() { ... } Static can also be applied to functions. Functions are extern by default - only their function prototype is needed in another file. The static keyword limits the scope of a function to just the file in which it is written. This feature is important when writing libraries. There should be no name clashes of small private functions. C++
159.234 Namespace C++ C++ extends the idea of limiting access to particular functions by use of namespace and the scope resolving operator :: Writers of libraries enclose their code with a namespace identifier
159.234 Namespace or by making all functions in the library directly accessible : using namespace PKlib; int main() { fct(); } Writers of libraries enclose their code with a namespace identifier namespace PKlib{ void fct(); } Users of this library can either use the function by using its full name: int main() { PKlib::fct(); } C++
Namespace Header files included without the .h prefix conform to the namespace standard. All the standard functions are in the “std” namespace. #include <iostream> #include <string> using namespace std; will enable a program to use all the console and string functions without a std:: prefix
Namespace Any of the following is acceptable: Assume:#include <iostream> using namespace std; int main(){ cout<<“Hello”;} or using std::cout; int main(){ cout<<“Hello”;} or int main(){ std::cout<<“Hello”;} GCC (use to) accept: int main(){ cout<<“Hello”;} Non-C++ standard
Conditional compilation Avoid multiple inclusions of the same header file. Include an #ifndef directive in the header file. (read as “if-not-defined” ) //The file myheader.h contains: #ifndef MYHEADER_H #define MYHEADER_H //The declarations of //the header file follow here. #endif
Conditional compilation • When the preprocessor scans the file for the first time: • As the symbol MYHEADER_H is not yet defined, • The #ifndef condition succeeds and • all declarations are scanned. • The symbol MYHEADER_H is defined. • When the file is scanned for the second time during the same compilation: • As the symbol MYHEADER_H is defined, • All information between the #ifndef and • #endif directives is skipped. • This is important to prevent accidental recursive inclusion of files by the programmer.
#include <iostream> using namespace std; intmyInt =98; // this is a global namespace { //unnamed namespace double d=88.22; } namespace Example{ const double PI= 3.14159; const double E=2.71828; intmyInt =25; void printVal(); namespace Inner{//nested namespace enum years {past=1900,present=2004,future}; } } int main(){ cout <<"d= "<<d; //output value of unnamed namespace cout <<"\nglobal variable myInt "<< myInt; //output values of Example namespace cout <<"\nPI = "<< Example::PI << "\nE =" << Example::E<<"\nmyInt= "<<Example::myInt <<"\nFUTURE = "<<Example::Inner::future<<endl; Example::printVal(); //invoke printVal function return 0; } void Example::printVal(){ cout <<"\nInprintVal:\n"<<"myInt = "<< myInt <<"\nPI = "<< PI << "\nE = " << E <<"\nd = " << d<<"\n(global) myInt ="<<::myInt <<"\nfuture = "<<Inner::future<<endl; } Example output: d= 88.22 global variable myInt 98 PI = 3.14159 E =2.71828 myInt= 25 FUTURE = 2005 In printVal: myInt = 25 PI = 3.14159 E = 2.71828 d = 88.22 (global) myInt =98 future = 2005
159.234 assert #include <assert.h> void fct(int n) { assert(n<10); printf("n is less than 10!\n"); } assert has a bool as an argument: If it is true then assert does nothing. If it is false then assert prints an error and exits. C++
Summary Static variable - a variable that is local to a function and whose lifetime is the life of the program. Namespaces are used to avoid name clashes when programs written by various parties are combined The unary operands new and delete are available to manipulate heap memory. Next: Classes (Textbook p.115)
159.234 new and delete int main() { int *ip; //allocate an array of int ip = new int[50]; ... delete [] ip; } notice the form of the delete with arrays. Don’t try to free something that was obtained with new or vice-versa. new and delete can be used instead of malloc and free. Use only one form or the other. int main() { int *ip; //allocate space for an int ip = new int; ... delete ip; } C++
Two Dimensional arrays • For example: int ** array; // declare an array of arrays - an array of “pointers-to-ints” array = new int* [10]; // allocate an array of pointers for(int i=0;i<10;i++){ // allocate individual arrays of ints array[i] = new int [7]; } array[9][6] = 42; // use as normal for(int i=0;i<10;i++){ // free up memory using delete[] operator delete[] array[i]; } delete[] array; // finally free up the array of arrays using delete []