C++ Functions Fred Kuhns fredk@cse.wustl.edu Applied Research Laboratory, Department of Computer Science and Engineering, Washington University in St. Louis Washington WASHINGTON UNIVERSITY IN ST LOUIS Procedural-based Programming Every C++ program contains (starts with) a main() function. The main() function may in turn call other user defined funtions Functions encapsulates an algorithm which operates on data passed in as parameters or explicit access to globally scoped data An object s scope determines its visibility (where it can be used) C++ defines several useful facilities related to functions: overloaded function definitions function prototypes exceptions STL and generic algorithms Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 2 what s a functions Function prototype <return type> function_name(<parameter type list>); Function definition <return type> function_name(<parameter list>){<function body>} inline ed functions inline <return type> function_name(<parameter_list>){<function body>} Example protocype and definition int min(int, int); int min(int a, int b) {return (a < b? a : b);} may return any built-in or user defined type. Can not return and array or function (but may return a pointer to one) Example function return types: void print_all(myclass_t c); Date &today(void); Node *get_last(head &); float sq(float, float); Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 3
Function arguments C++ is strongly typed: compiler verifies arguments are of the proper type (at compile time, not run time) Arguments: Arguments (and local variables) are allocated space on the run-time stack (function specific storage: activation record) Non-reference arguments are initialized by copy. Default passed-by-value, but may explicitly declare parameters to be passed by references. int min1(int a, int b){a = a<b?a:b;return a} int min2(int& a, int b){a = a<b?a:b;return a} Arrays as arguments int sum(int a[]); but how many elements in a? can use a vector, pass size or protocol int sum(vector<int> vec); // can use vec.size() int sum(int *a, size_t len); // there are len elements in a int sum(int *a) { int sum=0; while(*a!= -1) sum += *a; return *a;} Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 4 More bout functions Return value is pass-by-value, unless return type is void return; // nothing is returned return f; // a copy of object f is returned What about this defintion? value_t &lookup(key_t key) { code to locate key in table ; return table{key}.value}; Is there anything wrong with the following? myclass &getval(void){myclass m; ; return m}; Watch for side effects int &getval(const vector<int> &v, size_type i){return v[i]}; Use const to guard against unexpected resultsconst int &getval(vector<int> &v, int i){return v[i]}; function pointers int getval(int *a, int i){return v[i]}; int (*fp)(int *, int), x[10]; fp = getval; fp(x, 10); Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 5 Argument Examples Functions specifying const arguments. Does the const for parameter a make a difference to the caller? When considering viable functions during overload resolution? int fn(const int a) {return 2 * a;} int x = 4, w = fn(x); // ok const variables int fn(int a) {a = 5 * 4; return a;} const int x = 5; int w = fn(x); // error Overhead in function calls struct X_t {int st_; size_t len_; int buf_[64];} int sum(x_t x); X_t xx; /* fill in xx */ ; int s = sum(xx); Default behavior is to make a copy of the argument, in the above case this would be very expensive. Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 6
Local Variables local variables are placed on the stack, consequently every function call receives its own copy of local variables int sum(int x) {int sum = 0; sum += x; return sum;} // always returns the value of x You can declare a local variable to be static: int sum(int x) { static int sum = 0; sum += x; return sum; } // keeps a running total Declaring a local variable to be static causes a single statically allocated object to be created and used. there is only one object it retains it s value across function calls only initialized once. Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 7 Call by Reference Attempt 2: struct X_t {int st; size_t len; int buf[64];} int sum(x_t &x); X_t xx = { }; int s = sum(xx); In this case the compile must only bind the functions local argument symbol to the address of the caller s argument. Much cheaper than the earlier copy. We could also do this with pointers struct X_t {int st; size_t len; int buf[64];} int sum(x_t *x); X_t xx = { }; int s = sum(&xx); Additional advantage to references is some classes may not permit the copy operation. We can use similar reasoning for using references (or pointers) for return values. Generally in C++ you should prefer references over pointers. Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 8 More on references Using const references int fn(const X_t x); Generally const references are the safest way to go for a few reasons: I can be called with both lvalues and rvalues, specifically it can be called with temporary objects. assures the caller that their object will not be modified therefore avoiding unexpected side effects. the function may be used for both const and non-const objects. Examples form the book: int incr(int &v) {return ++val;} int main() {short v1 = 0; const int v2 = 42; int v3 = incr(v1); v3 = incr(v2); v3 = incr(0); v3 = incr(v1+v2); int v4 = incr(v3); } Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 9
Default Argument values You may provide default initializers for argument values int fn(int x, int y=0, int z=0); int a = fn(2); // y and z initialized to 0 int b = fn(3, 4); // z initialized to 0 c = fn(2, 0, 0); // all values explicit int d = fn(); // error, at least the first arg // must be specified Arguments are associated with parameters by position int fn(int x, double y=0, char z=0); int a = fn(23, c, 5.6); // logic error Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 10 inlining (from ~cdgill s notes) A function call uses the program call stack 1. Stack frame pushed when the call is made 2. Execution jumps to the function s code block 3. Function s code block is executed 4. Execution returns to just after where call was made 5. Stack frame is popped This incurs a (small) performance cost Copying arguments, other info into the stack frame Stack frame management Copying function result back out of the stack fr Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 11 Inlining (~cdgill) Considerations: If the function body is small, Computer might do more work manipulating the stack frame than in the actual function Classic computer science tradeoff Alternatives: can ask the compiler to use function inlining Intention: a directive, sometimes just a request Compiler makes an in line copy of the code at each place where it s called Code is inserted once for every function call made Especially good for small repetitive function calls Class/struct member accessors and mutators Empty or very simple implementations Benefits and Costs of Inlining call frequency, space requirements, performance Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 12
Inlining (~cdgill) The inline keyword is just a suggestion to the compiler Increase portability: make inlining requests optional using preprocessor directives Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 13 Member Functions Similar to standard functions, you must specify the return type, name, parameter list and body. Function prototype must appear in the class body. The function body may be defined separately. If default constructor is not specified then one is generated for you. (synthesized default constructor) class Complex { private: double re_; double im_; public: // Constructor initializer list : re_(0), im_(0) Complex() : re_(0), im_(0) {}; // Default constructor Complex(const Complex& c) : re_(c.re_), im_(c.im_) {}; // copy constructor Complex(double a, double b = 0) : re_(a), im_(b) {}; double real() const {return re_;} void real(double r) {re_ = r;} }; double imag() const {return im_;} void imag(double i) {im_ = i;} Complex & operator+=(const Complex &rhs){re_ += rhs.re_; im_ += rhs.im_;return *this;} Complex & operator-=(const Complex &rhs) {this->re_ -= rhs.re_; this->im_ -= rhs.im_; return *this;} Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 14 Overloading Same operator or function name but with different arguments Both operators and functions can be overloaded Signature consists of operator/function name and the number and types of its arguments in order E.g., add(int, long) and add(long, int) have different signatures Overriding a base class method is also a form of overloading But definitions are separated by scopes Compiler can distinguish (dynamically or statically, depending on whether virtual or not) Overloading is resolved by Finding all possible matches based on passed arguments Finding the best match among those Signature does not depend on return type So, overloading can t be resolved by return type alone Functions declared in different non-namespace scopes do not overload with namespaces using declarations can be used to control overloading Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 15
Finding a match Overload resolution, return type is not considered so result is not context dependent: 1. Best match: finding only one meeting all criteria 2. No match: not finding any matching functions 3. Ambiguous: more than one match is found The 3 steps 1. Find candidates: same name, visible in the current scope 2. Viable functions: have the same number of parameters and the types either match or are convertible. 3. Determine the best match: of the viable functions select the one with the closest matching argument types. With multiple arguments there is a match if 1. match for each argument is no worse than match required for any other viable function and 2. there is at least one argument with a better match than all other viable functions. Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 16 Argument Type Conversions Ranking of conversions 1. Exact match: exact or trivial conversions 1. no conversion 2. array name to pointer 3. function name to function pointer 4. T to const T 2. Promotion: See text for a complete list 1. (bool, char, short) to int -- signed and unsigned 2. float to double 3. double to long double 3. Standard conversion: see book for a complete list 1. int to double 2. double to int 3. Derived* to Base* 4. T* to void* 5. int to unsigned int 4. Class-type conversion: user defined conversions 5. match using elipses in function declaration const parameters only matter for reference and pointer arguments. Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 17 Helping the Compiler out Overloaded function void f1(char); void f1(long); void f2(char*); void f2(int*); void k(int i) { f1(i); // ambiguous: i promotes to int, and // has conversions to both char and long. f2(0); // ambiguous: literal is int or void pointer with // conversions to both char* and int* } To resolve the ambiguity you could add a wrapper function or specify the type: inline void f1(int n) {f1(long(n));} f2(static_cast<int*>(0)); Fred Kuhns (2/9/2006) CS422 Operating Systems Concepts 18