Basic knowledge
A function must be declared before it can be called (used). The declaration of the function allows the compiler to check whether the subsequent usage is correct - whether there are enough parameters, whether the parameter type is correct, and so on. A function declaration does not have to provide a function body, but it must indicate the return type, the function name, and the list of arguments. This is called function prototype.
If the return type of a function is not void, it must return a value at each possible exit point. Each return statement in a function is used to explicitly indicate that it is the exit point of the function. If the last statement in the function body is not return, then the last statement is followed by the implicit exit point of the function.
When we pass in an object as a function parameter by reference, the object itself does not copy another copy - the address of the object. Any operation on the object in the function is indirect to the incoming object. One of the reasons to declare a parameter as a reference is that you want to be able to modify the incoming object directly; the second reason is to reduce the extra burden of copying large objects.
We can also pass function parameters in the form of pointer, which is the same as the effect of reference: the address of the object is passed, not the copy of the whole object. But they are used differently. The more important difference between the pointer parameter and the reference parameter is that the pointer may or may not point to an actual object. When we collect a pointer, we must first make sure that its value is not 0. As for reference, it must represent an object, so this check is not necessary.
But the object allocated by using delete to release heap (heap, dynamic memory) is that there is no need to check that the pointer is zero, and the compiler will do this check automatically. If, for some reason, the programmer does not want to use the delete expression, the objects allocated by heap will never be released, which is called memory leak.
When choosing whether to use pointer or reference as function parameter, we need to know that pointer can be set to 0, which means that it does not point to any object, while reference must represent an object and cannot be set to 0.
Declaring the function as inline requires the compiler to expand the content of the function at each function call point. In the face of an inline function, the compiler can replace the call operation of the function with a copy of the function code, which will improve our performance. Specifying the function as inline is only a requirement for the compiler, but not mandatory. Whether the compiler executes the request depends on the compiler's situation.
Function overloading: two or more functions with different parameter lists (different parameter types or different parameter numbers) can have the same function name. The compiler will compare the actual parameters provided by the caller with the parameters of each overloaded function to find the most appropriate one. The compiler cannot distinguish two functions with the same name according to the function return type, because the return type cannot guarantee to provide us with a context sufficient to distinguish different overloaded functions.
There can be only one definition of a function, and many declarations. We do not put the definition of the function into the header file, because multiple code files of the same program may contain this header file. The exception to the rule of "define one copy only": the definition of inline function. In order to extend the content of inline function, the compiler obtains its definition at each call point, which means that we must put the definition of inline function in the header file instead of in different program code files; const Like the inline function, object is an exception under the rule of "define only one copy". The definition of const object is invisible as long as it is outside a file, which means that we can define it in multiple program code files without any errors. (note that the pointer to const object may not be const object itself.)
When referring to the header file, if the header file and the file containing the program code are located in the same disk directory, we use double quotation marks. If they are in different disk directories, we use angle brackets. The more technical answer is that if this file is identified as a standard or project specific header file, we will enclose the file name in angle brackets. When the compiler searches for this file, it will first search in some default disk directories. If the file name is surrounded by pairs of double quotes, it will be considered as a user provided header file. When searching for this file, it will be The disk directory where the file containing this file is located is starting to be found.
Exercise answer
Exercise 2.1 the previous main () let the user enter only one location value, and then the program ends. If the user wants to get two or more element values, it must execute the program twice or more times. Rewrite main() so that it allows the user to continue entering location values until the user wants to stop.
fibon_elem.h bool fibon_elem(int pos, int& elem) { if (pos <= 0 || pos > 45) { elem = 0; return false; } elem = 1; int n_2 = 1, n_1 = 1; for (int ix = 3;ix <= pos;++ix) { elem = n_2 + n_1; n_2 = n_1; n_1 = elem; } return true; } #include <iostream> #include "fibon_elem.h" using namespace std; extern bool fibon_elem(int, int&); int main() { int pos, elem; char ch; bool more = true; while (more) { cout << "Please enter a position: "; cin >> pos; if (fibon_elem(pos, elem)) { cout << "element # " << pos << " is " << elem << endl; } else { cout << "Sorry. Counld not calculate element # " << pos << endl; } cout << "would you like to try again? (y/n) "; cin >> ch; if (ch != 'y' && ch != 'Y') { more = false; } } return 0; }
Exercise 2.2 the evaluation formula of the pentagonal sequence is P(n)=n(3n-1)/2, which produces the element values of 1, 5, 12, 22, 35, etc. Try to define a function. Use the above formula to put the generated elements into the vector passed in by the user. The number of elements is specified by the user. Check the validity of the number of elements (too large to cause overflow problems). Next, write a second function that prints out all the elements of a given vector one by one. The second parameter of this function takes a string representing the type of the sequence stored in the vector. Finally, write a main() to test the above two functions.
#include <iostream> #include <vector> #include <string> using namespace std; bool calc_elements(vector<int>& vec, int pos); void display_elems(vector<int>& vec, const string& title, ostream& os = cout); int main() { vector<int> pent; int pos; const string title("Pentagonal Numeric Series"); cout << "Please enter a position: "; cin >> pos; if (calc_elements(pent, pos)) { display_elems(pent, title); } return 0; } bool calc_elements(vector<int>& vec, int pos) { if (pos <= 0 || pos > 100) { cerr << "Sorry. Invaild position: " << pos << endl; return false; } for (int ix = vec.size() + 1;ix <= pos;++ix) { vec.push_back(ix * (3 * ix - 1) / 2); } return true; } void display_elems(vector<int>& vec, const string& title, ostream& os) { os << '\n' << title << "\n"; for (int ix = 0;ix < vec.size();++ix) { os << vec[ix] << '\t'; if ((ix + 1) % 10 == 0) { cout << endl; } } os << endl; }
Exercise 2.3 divide the Pentagonal sequence evaluation function in exercise 2.2 into two functions, one of which is inline, to check whether the number of elements is reasonable. If it's reasonable, and it's not yet evaluated, the second function performs the actual evaluation.
#include <iostream> #include <vector> #include <string> using namespace std; void really_calc_elems(vector<int>& , int ); inline bool calc_elems(vector<int>&, int); void display_elems(vector<int>& vec, const string& title, int, ostream& os = cout); int main() { vector<int> pent; int pos; char ch; bool more = true; const string title("Pentagonal Numeric Series"); while (more) { cout << "Please enter a position: "; cin >> pos; if (calc_elems(pent, pos)) { display_elems(pent, title, pos); } cout << "would you like to try again? (y/n) "; cin >> ch; if (ch != 'y' && ch != 'Y') { more = false; } } return 0; } inline bool calc_elems(vector<int>& vec, int pos) { if (pos <= 0 || pos > 100) { cerr << "Sorry. Invalid position: " << pos << endl; return false; } if (vec.size() < pos) { really_calc_elems(vec, pos); } return true; } void really_calc_elems(vector<int>& vec, int pos) { for (int ix = vec.size() + 1;ix <= pos;++ix) { vec.push_back((ix * (3 * ix - 1) / 2)); } } void display_elems(vector<int>& vec, const string& title, int pos, ostream& os) { os << '\n' << title << "\n"; for (int ix = 0;ix < pos;++ix) { os << vec[ix] << '\t'; if ((ix + 1) % 10 == 0) { cout << endl; } } os << endl; }
Exercise 2.4 write a function to store the Pentagonal sequence elements in a local static vector. This function returns a const pointer to the vector. If the size of the vector is less than the specified number of elements, expand the size of the vector. Next, we implement the second function, which takes a location value and returns the element at that location. Finally, write main() to test these functions.
#include <iostream> #include <vector> using namespace std; inline bool check_validity(int pos); const vector<int>* pentagonal_series(int pos); bool pentagonal_elem(int pos, int& elem); int main() { int pos, elem; char ch; bool more = true; while (more) { cout << "Please enter a position: "; cin >> pos; if (pentagonal_elem(pos, elem)) { cout << "element " << pos << " is " << elem << endl; } cout << "would you like to continue? (y/n) "; cin >> ch; if (ch != 'y' && ch != 'Y') { more = false; } } return 0; } inline bool check_validity(int pos) { return (pos <= 0 || pos > 100) ? false : true; } const vector<int>* pentagonal_series(int pos) { static vector<int> _elems; if (check_validity(pos) && (pos > _elems.size())) { for (int ix = _elems.size() + 1;ix <= pos;++ix) { _elems.push_back((ix * (3 * ix - 1)) / 2); } } return &_elems; } bool pentagonal_elem(int pos, int& elem) { if (!check_validity(pos)) { cout << "Sorry. Invalid position: " << pos << endl; elem = 0; return false; } const vector<int>* pent = pentagonal_series(pos); elem = (*pent)[pos - 1]; return true; }
Exercise 2.5 implement an overloaded max() function to accept the following parameters: (a) two integers, (b) two floating-point numbers, (c) two strings, (d) an integer vector, (e) a floating-point number vector, (f) a string vector, (g) an integer array, and an integer value representing the array size, (h) a floating-point array, and an integer value representing the array size, (i) An array of strings and an integer value representing the size of the array. Finally, write main() to test these functions.
#include <iostream> #include <string> #include <vector> #include <algorithm> using namespace std; inline int max(int t1, int t2) { return t1 > t2 ? t1 : t2; } inline float max(float t1, float t2) { return t1 > t2 ? t1 : t2; } inline string max(string t1, string t2) { return t1 > t2 ? t1 : t2; } inline int max(const vector<int>& vec) { return *max_element(vec.cbegin(), vec.cend()); } inline float max(const vector<float>& vec) { return *max_element(vec.cbegin(), vec.cend()); } inline string max(const vector<string>& vec) { return *max_element(vec.cbegin(), vec.cend()); } inline int max(const int* parray, int size) { return *max_element(parray, parray + size); } inline float max(const float* parray, int size) { return *max_element(parray, parray + size); } inline string max(const string* parray, int size) { return *max_element(parray, parray + size); } int main() { int size = 5; int n = 1, n2 = 9; float f = 0.34, f2 = 9.3; string s = "dfsdg", s2 = "dafsdfsad"; vector<int> ivec = { 12,70,2,169,1,5,29 }; vector<float> fvec = { 2.5,24.8,18.7,4.1,23.9 }; vector<string> svec = { "we","were","her","pride","of","ten" }; int iarray[5] = { 1,2,3,4,5 }; float farray[5] = { 5.0,4.0,3.0,2.0,1.0 }; string sarray[5] = { "a","b","c","asfs","aaa" }; cout << "max(n,n2)=" << max(n, n2) << "\n" << "max(f,f2)=" << max(f, f2) << "\n" << "max(s,s2)=" << max(s, s2) << "\n" << "max(ivec)=" << max(ivec) << "\n" << "max(fvec)=" << max(fvec) << "\n" << "max(svec)=" << max(svec) << "\n" << "max(iarray,size)=" << max(iarray, size) << "\n" << "max(farray,size)=" << max(farray, size) << "\n" << "max(sarray,size)=" << max(sarray, size) << "\n"; return 0; }
Exercise 2.6 complete exercise 2.5 again with template, and make appropriate changes to the main() function.
#include <iostream> #include <string> #include <vector> #include <algorithm> using namespace std; template <typename Type> inline Type new_max(Type t1, Type t2) { return t1 > t2 ? t1 : t2; } template <typename elemType> inline elemType new_max(const vector<elemType>& vec) { return *max_element(vec.cbegin(), vec.cend()); } template <typename arrayType> inline arrayType new_max(const arrayType* parray, int size) { return *max_element(parray, parray + size); } int main() { int size = 5; int n = 1, n2 = 9; float f = 0.34, f2 = 9.3; string s = "dfsdg", s2 = "dafsdfsad"; vector<int> ivec = { 12,70,2,169,1,5,29 }; vector<float> fvec = { 2.5,24.8,18.7,4.1,23.9 }; vector<string> svec = { "we","were","her","pride","of","ten" }; int iarray[5] = { 1,2,3,4,5 }; float farray[5] = { 5.0,4.0,3.0,2.0,1.0 }; string sarray[5] = { "a","b","c","asfs","aaa" }; cout << "max(n,n2)=" << new_max(n, n2) << "\n" << "max(f,f2)=" << new_max(f, f2) << "\n" << "max(s,s2)=" << new_max(s, s2) << "\n" << "max(ivec)=" << new_max(ivec) << "\n" << "max(fvec)=" << new_max(fvec) << "\n" << "max(svec)=" << new_max(svec) << "\n" << "max(iarray,size)=" << new_max(iarray, size) << "\n" << "max(farray,size)=" << new_max(farray, size) << "\n" << "max(sarray,size)=" << new_max(sarray, size) << "\n"; return 0; }
end.
"It's only a matter of knowing how to do it."