!!!Chapter 7 Functions (7.4 ~ 7.9)

来源:互联网 发布:自己皮肤类型知乎 编辑:程序博客网 时间:2024/06/05 18:43

7.4 Function Declarations

A function must be declared before it is called. We can declare a function separately from its definition; a function may be defined only once but may be declared multiple times.

A function declaration contains: a return type, the function name, parameter list. These are referred asfunction prototype.The parameter list must contain parameter type, but need not name them.

Parameter names in declaration are ignored, but we can still have a name as a documentation aid:

void print(int * array, int size)    //indicate the first parameter is array ,the second is size of array

Function Declarations Go in Header Files

Like variables, functions should be declared in header files and defined in source files.

The source file that defines the function should include the header that declares the function. So that compiler can check the definition and declaration are the same.

7.4.1 Default Arguments

A default argument is a value that is expected to be used most of the time. When we call the function, we may omit any argument that has a default. The compiler will supply the default value for any argument we omit.

A default argument is specified by providing an explicit initializer for the parameter in the parameter list. We may define defaults for one or more parameters. However, if a parameter has a default argument, all the parameters that follow it must also have default arguments.

If an argument is provided, it overrides the default argument value; otherwise, the default argument is used.

Argument to the call are resolved by position, and default arguments are used to substitute for the trailing arguments of a call. So we must specify the first two arguments, if we want to specify the third.

string screenInit(string::size_type height = 24,                   string::size_type width = 80, char background = ' ');string screen;screen = screenInit();              //(24, 80, ' ')screen = screenInit(66);            //(66, 80, ' ')screen = screenInit(66,256);        //(66, 256, ' ')screen = screenInit(66, 256, 'a');screen = screenInit(, , '?');       //error, can omit only trialing argumentsscreen = screenInit('?');           //('?', 80, ' ')

Because char is an integral type, it is legal to pass a char to an int parameter and vice versa.

Default Argument Initializers

A default argument can be any expression of an appropriate type. Or it can be another function!

Constraints on Specifying Default Arguments

We can specify default arguments in either the function definition or declaration. however, a parameter can have its default argument specifiedonly once in a file:

//ff.hint ff(int = 0);//ff.cc#include "ff.h"int ff (int i = 0)         //error

1. Default arguments ordinarily should be specified with the declaration for the function and placed in a appropriate header.

2. If default argument is used in function definition, the default argument is available only for function calls in the source file that contains the function definition.

7.5 Local Objects

In C++, names have scope, and objects have lifetime.

Scope of a name is the part of the program's text in which that name is known. The lifetime of an object is the time during the program's execution that the object exists.

7.5.1 Automatic Objects

By default, the lifetime of a local variable is limited to the duration of a single execution of the function. Such objects are automatic objects. Automatic objects are created and destroyed on each call to a function.

Parameters are automatic objects. The storage in which the parameters reside is created when the function is called and is freed when the function terminates.

7.5.2 Static Local Objects

Variable that is in the scope of a function but whose lifetime persists across calls to the function are known as static local object. It is initialized no later than the first time the program execution passes through the object's definition. Once it's created, it is not destroyed until the program terminates.

size_t count_calls(){  static size_t ctr = 0;    //value will persist across calls  return ++ctr;}int main(){   for (size_t i = 0; i != 10; ++i)       cout << count_calls() << endl;   return 0;}                           //output is 1,2,...10

Here crt is initialized to 0 before count_calls is called for the first time.

Difference between parameter, local variable and static local variable:

1. A parameter is created upon entry to the scope of a function, initialized with the arguments passed when calling the function. Parameter variables are destroyed upon leaving the function scope.
2. A local variable is created at the point of declaration in it's scope, and is destroyed as soon as the program leaves the scope.
3. A static local variable is the same as a local variable (only visible in the local scope) except that it is not destroyed when exiting the function, and therefore it's value is persistent across all calls.

7.6 Inline Functions

Calling a function is slower than evaluating the equivalent expression.

A function specified as inline is expanded "in line" at each point in the program in which it is invoked.

If we make shorterString an inline function:

//inline version: find longer of two stringsinline const string & shorterString (const string &s1, const string &s2){   return s1.size() < s2.size() ? s1 : s2;} 

And we call this function:

cout << shorterString (s1, s2) << endl;

The function would be expanded during compilation into:

cout << (s1.size() < s1.size() ? s1 : s2) << endl;

The inline specification is only a request to the compiler. The compiler may choose to ignore this request.

Put inline Functions in Header Files

Unlike other function definitions, inlines should be defined in header files. (Not declare!)

An inline function may be defined more than once in a program as long as the definition appears only once in a given source file and the definition is exactly the same in each source file.

7.7 Class Member Functions

We define member functions similarly to how we define ordinary functions. A member function consists of four parts:

1. A return type for the function

2. The function name

3. A (possibly empty) comma-separated list of parameters

4. The function body, which is contained between a pair of curly braces

The first three of these parts constitute the function prototype. The function prototype must be defined within the class body. The body of the function, however, may be defined within the class itself or outside the class body.

7.7.1 Defining the Body of a Member Function

We must declare all the members of a class within the curly braces that delimit the class definition. Members that are functions must be defined as well as declared.

We can define a member function either inside or outside of the class definition.

class Sales_item {public:  //operations on Sales_item objects  double avg_price() const;      //function defined outside  bool same_isbn (const Sales_item &rhs) const    //function defined inside    {return isbn == rhs.isbn; }                   //can access isbn, though it's private  //private members  std::string isbn;  unsigned units_sold;  double revenue;};

A member function that is defined inside the class is implicitly treated as an inline function.

A member function may access the private members of its class

Member Functions Have an Extra, Implicit Parameter

When we call same_isbn on the object named total, that object is also passed to the function. When same_isbn refers to isbn, it is implicitly referring to the isbn member of the object on which the function is called:

if (total.same_isbn (trans))

In the function same_isbn, the unqualifiedisbn refers to theisbn member of the object on behalf of which the function is called.

Introducing this

Each member function (except static member function, 12.6 P 467) has an extra, implicit parameter named this. When a member function is called, the this parameter is initialized with the address of the object on which the function was invoked:

total.same_isbn(tran);//pseudo-code illustration of how a call to a member function is translatedSales_item::same_isbn (&total, tran); 

In the above call, the data member isbn insidesame_isbn is bound to the one belonging tototal.

Introducing const Member Functions

The const (which follows the parameter lists in the declarations of Sales_item) is used to modify the type of the implicit this parameter. So when we call Sales_item, the implicit this parameter will be a const Sales_Item* that points tototal.

//pseudo-code when call sales_item functionbool Sales_item::same_isbn (const Sales_item *const this,      //the first const means the target variable is const,                             const Sales_item &rhs)             //the second means the pointer is a const{return (this->isbn == rhs.isbn);} 

Here const is used to modify the first const in line 2.

A function that uses const in this way is called a const member function. A const member function cannot change the object on whose behalf the function is called.Thus,avg_price and same_isbn may read but not write to the data members of the objects on which they are called.

const object (or pointer/reference to const object) can only call const member functions.

7.7.2 Defining a Member Function Outside the Class

Member functions defined outside the class definition must indicate that they are members of the class.:

double Sales_item::avg_price() const{    if (units_sold)        return revenue/units_sold;    else        return 0;}

The function name Sales_item::avg_price indicate that we are defining the function named avg_price in the scope of the Sales_item class.

The const that follows the parameter list reflects the way we declared the member function inside the Sales_item header. (we must ensure the return type and parameter list match the declaration of the function).

7.7.3 Writing the Sales_item Constructor

Class data members are not initialized when the class is defined. They are initialized through a constructor (2.8 P65). So we need to define constructor in the class definition.

Constructors Are Special Member Functions

A constructor is a special member function that have the same name as its class. And constructor has no return type.

Constructor take a parameter list and have a function body.

A class can have multiple constructors. Each constructor must differ from others in the number or types of its parameters.

The default constructor is the one thattakes no arguments. The default constructor says what happens when we define an object but do not supply an (explicit) initializer.

Defining a Constructor

A constructor is declared inside the class and may be defined there or outside the class.

class Sales_item {public://default constructor needed to initialize members of built-in typeSales_item(): units_sold (0), revenue (0.0) {}}

We put the constructor in the public section. If we made the constructor private, it would not be possible to define Sales_item objects, which would make the class pretty useless.
The above constructor means we defining a constructor that has an empty parameter list and an empty function body.

Constructor Initialization List

The colon and the following text up to the open curly is the constructor initializer list. It specifies initial values for one or more data members of the class. It follows the constructor parameter list and begins with a colon.

The constructor initializer is a list of member names, each of which is followed by that member's initial value in parentheses. Multiple member initializations are separated by commas.

We don't need to initialize isbn. Unless we say otherwise in the constructor initializer list, members that are of class initialized by the string default constructor.(we only need to initialize built-in type!)

Here the parameter list is empty because we are defining the constructor that is run by default, when no initializer is present. The body is empty because there is no work to do.

Synthesized Default constructor

The compiler-created default constructor is known as a synthesized default constructor. It will initialize members of class type by the default constructor of that class, and initialize built-in type depends on the scope of the member.

synthesized default constructor is good for classes that only contain member of class type. But we should have default constructor if the class have built-in type.

7.7.4 Organizing Class Code Files

Class declarations ordinarily are placed in headers. Usually, member functions defined outside the class are put in ordinary source files. 

The class definition is put in a file named type.h ortype.H, where type is the name of the class defined in the file. 

Member function definitions usually are stored in a source file whose name is the name of the class.

So we should put the definition of our Sales_item functions in a file named Sales_item.cc.

7.8 Overloaded Functions

Two functions that appear in the same scope are overloaded if they have the same name but have different parameter lists.

We may define a set of functions that perform the same general action but that apply to different parameter types.

There may be only one instance of main in any program. The main function may not be overloaded.

Distinguishing Overloading from Redeclaring a Function

If the return type and parameter list of two functions declarations match exactly, then the second declaration is aredeclaration of the first.

If the parameter lists of two functions match exactly but the return types differ, then the second declaration is anerror:

Record lookup (const Account&);bool lookup (const Account&);      //error: only return type is different

Two parameter lists can be identical, even if they don't look the same:

Record lookup (const Account &acct);Record lookup (const Account &);      //parameter names are ignoredtypedef Phone Telno;Record lookup (const Phone&);Record lookup (const Telno&);         //Telno and phone are the same typeRecord lookup (const Phone&, const Name&);Record lookup (const Phone&, const Name& = " ");  //default argument doesn't change the number of parametersRecord lookup (Phone);Record lookup (const Phone);          //const is irrelavent for nonreference parameters

7.8.1 Overloading and Scope

A name declared local to a function hides the same name declared in the global scope. The same is true for function names and variable names:

string init();            //init has global scopevoid fcn(){   int init = 0;          //init is local and hides global init   string s = init ();    //error: global init is hidden}

If we declare a function locally, that function hides rather than overloads the same function declared in an outer scope. As a consequence,declarations for every version of an overloaded function must appear in the same scope.
In general, it is a bad idea to declare a function locally. Function declarations should go in header files.

7.8.2 Function Matching and Argument Conversions

Function overload resolution (function matching) is the process by which a function call is associated with a specific function from a set of overloaded functions.

There are three possible outcomes:

1. The compiler finds one function that is a best match and generates code to call that function

2. There is no function with parameters that match the arguments in the call, in which case the compiler indicates a compile-time error.

3. There is more than one function that matches and none of them is clearly best. It is also an error; the call is ambiguous.

7.8.3 The Three Steps in Overload Resolution

void f();void f(int);void f(int, int);void f(double, double = 3.14);f(5.6);    //call void f(double, double)

Candidate Functions

Candidate functions has the same name as the function that is called and the declaration is visible at the point of the call.

There are four candidate functions named f.

Determining the Viable Functions

To be viable, a function must meet two tests. First, the function must have the same number of parameters as there are arguments in the call. Second, the type of each argument must match - or be convertible to - the type of its corresponding parameter.

When a function has default arguments, a call may appear to have fewer arguments than it actually does.

So void f(int) and void f(double, double) are viable.

Finding the Best Match, If Any

"Best" means that the closer the types of the argument and parameter are to each other, the better the match.

Overload Resolution with Multiple Parameters

There is a match if there is one and only one function for which:

1. The match for each argument is no worse than the match required by any other viable function.

2. There is at least one argument for which the match is better than the match provided by any other viable function.

So f(42, 2.56) is ambiguous.

7.8.4 Argument-Type Conversions

To determine the best match, compiler ranks conversion in the following order:

1. An exact match. The argument and parameter types are the same

2. Match through a promotion

3. Match through a standard conversion

4. Match through a class-type conversion. (14.9 P 535)

Matches Requiring Promotion or Conversion

One important point to realize is that the small integral types promote to int. So theint version of parameter is always a better match for a value of any integral type other thanshort, even though short might appear on the surface to be a better match.

void ff(int);void ff(short);ff('a');     //char promotes to int, so matches f(int)

A char will be promoted to int so it will match f(int). A char can also converted to short, but convert is less good than promotion.

Promotion is better than other conversions. And all other conversions are treated equivalent. E.G. conversion from char to int is better than conversion from char to short; conversion from char to unsigned char takes no precedence over the conversion from char to double.

Promotion Hierarchy:

Long double (highest)
Double
Float
Unsigned long int
Long int
Unsigned int
Int (lowest)

Parameter Matching and Enumerations

An object of enum type may be initialized only by another object of that enum type or one of its enumerators. An integral object that happens to have the same value as an enumerator cannot be used to call a function expecting an enum argument:

enum Tokens { INLINE = 128, VIRTUAL = 129};void ff(Tokens);void ff(int);int main(){    ff(128);      //exactly matches ff(int)    ff(INLINE);   //exactly matches ff(Tokens)}

Although we cannot pass an integral value to a enum parameter, we can pass an enum to a parameter of integral type bypromotion.

Overloading and const Parameters

Whether a parameter is const only matters when the parameter is a reference or pointer.

We can overload a function based on whether a reference parameter refers to a const or nonconst type.

If the object is const, then we can only pass it to const reference parameter.

If the object is nonconst, then we can pass it to both const/nonconst reference parameters. However, it requires a conversion when passing a nonconst to const reference.

Pointer behaves similarly to references.

We cannot overload based on whether the pointer itself is const:

f(int *);          f(const int *);   //overload of the first functionf(int *const);    //redeclaration of the first function

7.9 Pointers to Functions

A function pointer is just a pointer that denotes a function rather than an object.

A function's type is determined by its return type and its parameter list. A function's name is not part of its type:

//pf points to a function returning bool that takes two const string referencesbool (*pf) (const string &, const string &);//declares a function named pf that returns a bool *bool *pf (const string &, const string &);

Using Typedefs to simplify Function Pointer Definitions

typedef bool (*cmpFcn) (const string &, const string &);

This definition says that cmpFcn is the name of a type that is a pointer to function.

Initializing and Assigning Pointers to Functions

When we use a function name without calling it (not calling means do not use () signal), then name is automatically treated as a pointer to a function.

A function pointer may be initialized or assigned by a function or function pointer that has the same type or by a zero-valued constant expression:

bool length (const string &, const string &);cmpFcn pf1 = 0;      //ok: unbound pointer to functioncmpFcn pf2 = length; //ok: pointer type matches function's typepf1 = length;        //ok: pointer type matches function's typepf2 = pf1;           //ok: pointer types match

Using the function name is equivalent to applying the address-of operator to the function name:

cmpFcn pf1 = length;cmpFcn pf2 = &length;

There is no conversion between one pointer to function type and another(different return type or different parameter type):

bool cclength (char *, char *);cmpFcn pf;pf = cclength;   //error: parameter teyps differ

Calling a Function through a Pointer

A pointer to a function can be used to call the function to which it refers. We can use the pointer directly - there is no need to use the dereference operator:

length ("hi", "bye");   //direct callpf ("hi", "bye");       //equivalent call: pf implicitly dereferenced(*pf) ("hi", "bye");    //equ8ivalent call: pf explicitly dereferenced

Function Pointer Parameters/Returning a Pointer to Function/Pointers to Overloaded FunctionsP278