日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

读书笔记||函数探幽

發布時間:2023/12/20 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 读书笔记||函数探幽 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、C++內聯函數

內聯函數是C++為提高程序運行速度所做的改進。常規函數和內聯函數之間的主要區別不在于編寫方式,而在于C++編譯器如何將他們組合到程序中。
編譯過程的最終產品是可執行程序。運行程序時,操作系統將這些指令載入到計算機內存中,因此每條指令都有特定的內存地址。計算機隨后將逐步執行這些指令。有時候循環或者分支語句,將跳過一些指令,向前或向后跳到特定的地址。常規函數調用也使程序跳到另一個函數的地址,并在函數結束時返回。執行函數調用指令時,程序將在函數調用后立即存儲該指令的內存地址,并將函數參數復制到堆棧,保留內存塊,跳到標記函數起點的內存單元,執行函數代碼(也許還需將返回值放入寄存器中),然后跳回到地址被保存的指令處。
C++內聯函數提供了另一種選擇。內聯函數的編譯代碼與其他程序代碼的“內聯”起來,也就是說,編譯器將使用相應的函數代碼替換函數調用。對于內聯代碼,程序無需跳到另一個位置處執行代碼,再跳回來。因此,內聯函數的運行速度比常規函數稍快,但代價是需要占用更多的內存。
應該有選擇地使用內聯函數。如果執行函數代碼地時間比處理函數調用機制地時間長,則節省的時間將只占用整個過程的很小一部分。如果代碼執行時間很短,則內聯調用使用大部分時間。另一方面,由于這個過程相當快,因此節省了該過程的大部分時間,但節省的時間絕對值并不大,除非函數值經常被調用。
要使用這項特性,必須采取下述措施之一:
1.在函數聲明前加上關鍵字inline;
2.在函數定義前加上關鍵字inline。
通常的做法是省略原型,將整個定義(即函數頭和所有函數代碼)放在本應提供原型的地方。

#include <iostream> // an inline function definition inline double square(double x) { return x * x; } int main() {using namespace std;double a, b;double c = 13.0;a = square(5.0);b = square(4.5 + 7.5); // can pass expressionscout << "a = " << a << ", b = " << b << "\n";cout << "c = " << c;cout << ", c squared = " << square(c++) << "\n";cout << "Now c = " << c << "\n";// cin.get();return 0; }

內聯函數和常規函數一樣,也是按值來傳遞的,如果參數為表達式即4.5+7.5,則函數將傳遞表達式的值12。這使得C++內聯功能遠遠勝過C語言的宏定義。
盡管程序沒有提供獨立的原型,但C++原型特性仍在起作用。這是因為在函數首次使用前出現整個函數定義充當了原型,這意味著可以給square()傳遞int和long值,將值傳遞給函數前,程序自動將這個值強制轉換為double類型。

二、引用變量

C++新增了一種復合類型——引用變量。引用是已定義的變量的別名,如果將twain作為clement變量的引用,則可以交替使用twain和clement來表示該變量。引用變量的主要用途是用作函數的形參。通過將引用變量用作參數,函數將使用原始數據,而不是其副本,這樣除指針之外,引用也為函數處理大型結構提供了一種非常方便的途徑,同時對于設計類來說,引用是必不可少的。
1.創建引用變量
C和C++使用&符號來指示變量的地址。C++給&符號賦予了另一個含義,將其用來聲明引用。

int rats; int & rodents = rats; //makes rodents an alias for rats

其中,&不是地址運算符,而是類型標識符的一部分。就像聲明中的char*指的是指向char的指針一樣,int&指的是指向int的引用。

#include <iostream> int main() {using namespace std;int rats = 101;int & rodents = rats; // rodents is a referencecout << "rats = " << rats;cout << ", rodents = " << rodents << endl;rodents++;cout << "rats = " << rats;cout << ", rodents = " << rodents << endl; // some implementations require type casting the following // addresses to type unsignedcout << "rats address = " << &rats;cout << ", rodents address = " << &rodents << endl;// cin.get();return 0; }


int & rodents = rats;中的&運算符不是地址運算符,而是將rodents的類型聲明為int&,即指向int變量的引用,
但是, cout << ", rodents address = " << &rodents << endl;中的&的運算符是地址運算符,其中&rodents表示rodents引用的變量的地址。
在上述的例子中,rats和rodents的值和地址都相同,將rodents加1將影響這兩個變量的值,更準確的講rodents++操作講一個有兩個名稱的變量加1。

int rats = 101;int & rodents = rats; // rodents is a referenceint * prats = &rats; // parts a pointer

表達式rodents和*prats都可以同rats互換,而表達式&rodents和prats都可以同&rats互換。從這一點來講,引用看上去很像偽裝表達式的指針,但是引用又不同于指針,除了表示法不同以外,差別之一就是必須在聲明引用時將其初始化,而不能像指針那樣,先聲明,再賦值。

int rat; int & rodent; rodent = rat; //No,you can't do this. //必須在聲明引用變量時進行初始化。

引用更接近const指針,必須在創建時進行初始化,一旦與某個變量關聯起來,就將一直效忠于它,也就是說:
int & rodents = rats ;
實際上是下述代碼的偽裝表示:
int * const pr =&rats;

#include <iostream> int main() {using namespace std;int rats = 101;int & rodents = rats; // rodents is a referencecout << "rats = " << rats;cout << ", rodents = " << rodents << endl;cout << "rats address = " << &rats;cout << ", rodents address = " << &rodents << endl;int bunnies = 50;rodents = bunnies; // can we change the reference?cout << "bunnies = " << bunnies;cout << ", rats = " << rats;cout << ", rodents = " << rodents << endl;cout << "bunnies address = " << &bunnies;cout << ", rodents address = " << &rodents << endl;// cin.get();return 0; }

最初,rodents引用的是rats,隨后程序試圖將rodents作為bunnies的引用:rodents=bunnies;
這種意圖是成功的,因為rodents的值從101變為了50。但是同時rats也變成了50,同時rats和rodents的地址也相同,而該地址與bunnies的地址不同。由于rodents是rats的別名,因此上述賦值語句與下面的語句等效:rats = bunnies;
也就是說,這意味著將bunnies變量的值賦給rat變量,簡而言之,可以通過初始化聲明來設置引用,但是不能通過賦值來設置。
2.將引用用作函數參數
引用經常被用作函數參數,使得函數中的變量名成為調用程序中的變量的別名。這種傳遞參數的方法稱為按引用傳遞。按引用傳遞允許被調用的函數能夠訪問調用函數中的變量。
交換兩個變量的值:交換函數必須能夠修改調用程序中變量的值。這意味著按值傳遞變量將不管用,因為函數將交換原始變量副本的內容,而不是變量本身的內容。但是傳遞引用時,函數可以使用原始數據,另一種方法是,傳遞指針來訪問原始數據。

#include <iostream> void swapr(int & a, int & b); // a, b are aliases for ints void swapp(int * p, int * q); // p, q are addresses of ints void swapv(int a, int b); // a, b are new variables int main() {using namespace std;int wallet1 = 300;int wallet2 = 350;cout << "wallet1 = $" << wallet1;cout << " wallet2 = $" << wallet2 << endl;cout << "Using references to swap contents:\n";swapr(wallet1, wallet2); // pass variablescout << "wallet1 = $" << wallet1;cout << " wallet2 = $" << wallet2 << endl;cout << "Using pointers to swap contents again:\n";swapp(&wallet1, &wallet2); // pass addresses of variablescout << "wallet1 = $" << wallet1;cout << " wallet2 = $" << wallet2 << endl;cout << "Trying to use passing by value:\n";swapv(wallet1, wallet2); // pass values of variablescout << "wallet1 = $" << wallet1;cout << " wallet2 = $" << wallet2 << endl;// cin.get();return 0; } void swapr(int & a, int & b) // use references {int temp;temp = a; // use a, b for values of variablesa = b;b = temp; } void swapp(int * p, int * q) // use pointers {int temp;temp = *p; // use *p, *q for values of variables*p = *q;*q = temp; } void swapv(int a, int b) // try using values {int temp;temp = a; // use a, b for values of variablesa = b;b = temp; }


void swapr(int & a, int & b); //pass variables
void swapp(int * p, int * q); //pass addresses of variables
void swapv(int a, int b); //pass values of variables
按引用傳遞swapr和按值傳遞swapv看起來相同。只能通過原型或函數定義才能知道swapr是按引用傳遞。然而地址運算符&使得按地址傳遞swapp。
3.引用的屬性個特別之處

#include <iostream> double cube(double a); double refcube(double &ra); int main () {using namespace std;double x = 3.0;cout << cube(x);cout << " = cube of " << x << endl;cout << refcube(x);cout << " = cube of " << x << endl;// cin.get();return 0; } double cube(double a) {a *= a * a;return a; } double refcube(double &ra) {ra *= ra * ra;return ra; }


refcube()函數修改了main()中x的值,而cube()沒有,這提醒我們為何通常按值傳遞,變量a位于cube()中,它被初始化x的值,但修改a并不會影響x。但由于refcube()使用了引用參數,因此修改ra實際上就是修改x。
創建臨時變量:1.實參的類型是正確,但不是左值;2.實參的類型不正確,但可以轉換為正確的類型。
4.將引用用于結構
引用引入主要是為了用于結構和類,而不是基本的內置類型。
使用結構引用參數的方式與使用基本變量引用相同,只需在聲明結構參數時使用引用運算符&即可。

#include <iostream> #include <string> struct free_throws {std::string name;int made;int attempts;float percent; }; void display(const free_throws & ft); void set_pc(free_throws & ft); free_throws & accumulate(free_throws &target, const free_throws &source); int main() {free_throws one = {"Ifelsa Branch", 13, 14};free_throws two = {"Andor Knott", 10, 16};free_throws three = {"Minnie Max", 7, 9};free_throws four = {"Whily Looper", 5, 9};free_throws five = {"Long Long", 6, 14};free_throws team = {"Throwgoods", 0, 0};free_throws dup;set_pc(one);display(one);accumulate(team, one);display(team); // use return value as argumentdisplay(accumulate(team, two));accumulate(accumulate(team, three), four);display(team); // use return value in assignmentdup = accumulate(team,five);std::cout << "Displaying team:\n";display(team);std::cout << "Displaying dup after assignment:\n";display(dup);set_pc(four); // ill-advised assignmentaccumulate(dup,five) = four;std::cout << "Displaying dup after ill-advised assignment:\n";display(dup);// std::cin.get();return 0; } void display(const free_throws & ft) {using std::cout;cout << "Name: " << ft.name << '\n';cout << " Made: " << ft.made << '\t';cout << "Attempts: " << ft.attempts << '\t';cout << "Percent: " << ft.percent << '\n'; } void set_pc(free_throws & ft) {if (ft.attempts != 0)ft.percent = 100.0f *float(ft.made)/float(ft.attempts);elseft.percent = 0; } free_throws & accumulate(free_throws & target, const free_throws & source) {target.attempts += source.attempts;target.made += source.made;set_pc(target);return target; }


5.將引用用于類對象
將類對象傳遞給函數時,C++通常的做法是使用引用。可以通過使用引用,讓函數將類string、ostream、istream、ofstream和i發stream等類的對象作為參數。

#include <iostream> #include <string> using namespace std; string version1(const string & s1, const string & s2); const string & version2(string & s1, const string & s2); // has side effect const string & version3(string & s1, const string & s2); // bad design int main() {string input;string copy;string result;cout << "Enter a string: ";getline(cin, input);copy = input;cout << "Your string as entered: " << input << endl;result = version1(input, "***");cout << "Your string enhanced: " << result << endl;cout << "Your original string: " << input << endl;result = version2(input, "###");cout << "Your string enhanced: " << result << endl;cout << "Your original string: " << input << endl;cout << "Resetting original string.\n";input = copy;result = version3(input, "@@@");cout << "Your string enhanced: " << result << endl;cout << "Your original string: " << input << endl;// cin.get();// cin.get();return 0; } string version1(const string & s1, const string & s2) {string temp;temp = s2 + s1 + s2;return temp; } const string & version2(string & s1, const string & s2) // has side effect {s1 = s2 + s1 + s2; // safe to return reference passed to functionreturn s1; } const string & version3(string & s1, const string & s2) // bad design {string temp;temp = s2 + s1 + s2; // unsafe to return reference to local variablereturn temp; }


6.何時使用引用參數
主要原因:1.能夠修改調用函數中的數據類型;2.通過傳遞引用而不是整個數據對象,可以提高程序的運行速度。

三、默認參數

默認參數指的是當函數調用中省略了實參時自動使用的一個值。如果將void wow(int n)設置成n個有默認值為1,則函數調用wow()相當于wow(1)。這極大地提高了使用函數的靈活性。

#include <iostream> const int ArSize = 80; char * left(const char * str, int n = 1); int main() {using namespace std;char sample[ArSize];cout << "Enter a string:\n";cin.get(sample,ArSize);char *ps = left(sample, 4);cout << ps << endl;delete [] ps; // free old stringps = left(sample);cout << ps << endl;delete [] ps; // free new string// cin.get();// cin.get();return 0; } // This function returns a pointer to a new string // consisting of the first n characters in the str string. char * left(const char * str, int n) {if(n < 0)n = 0;char * p = new char[n+1];int i;for (i = 0; i < n && str[i]; i++)p[i] = str[i]; // copy characterswhile (i <= n)p[i++] = '\0'; // set rest of string to '\0'return p; }


該程序使用new創建一個新的字符串,以存儲被選擇的字符。

四、函數重載

默認參數是能夠讓使用不同數目的參數調用一個函數,而函數重載能夠使用多個同名的函數。“函數重載”指的是可以有多個同名的函數,因此對名稱進行重載。
函數重載的關鍵是函數的參數列表——也稱為函數特征標。如果兩個函數的參數數目和類型相同,同時參數的排列順序也相同,則它們的特征標相同,而變量名是無關緊要的。

#include <iostream> unsigned long left(unsigned long num, unsigned ct); char * left(const char * str, int n = 1); int main() {using namespace std;char * trip =(char *) "Hawaii!!"; // test valueunsigned long n = 12345678; // test valueint i;char * temp;for (i = 1; i < 10; i++){cout << left(n, i) << endl;temp = left(trip,i);cout << temp << endl;delete [] temp; // point to temporary storage}// cin.get();return 0; } // This function returns the first ct digits of the number num. unsigned long left(unsigned long num, unsigned ct) {unsigned digits = 1;unsigned long n = num;if (ct == 0 || num == 0)return 0; // return 0 if no digitswhile (n /= 10)digits++;if (digits > ct){ct = digits - ct;while (ct--)num /= 10;return num; // return left ct digits}else // if ct >= number of digitsreturn num; // return the whole number } // This function returns a pointer to a new string // consisting of the first n characters in the str string. char * left(const char * str, int n) {if(n < 0)n = 0;char * p = new char[n+1];int i;for (i = 0; i < n && str[i]; i++)p[i] = str[i]; // copy characterswhile (i <= n)p[i++] = '\0'; // set rest of string to '\0'return p; }


只有當函數基本上執行相同的任務,但使用不同形式的數據時,才采用函數重載。

五、函數模塊

函數模塊是通用的函數描述,也就是使用泛型來定義函數,其中泛型可用具體的類型(int或者double)替換。通過將類型作為參數傳遞給模板,也可以使用編譯器生成該類型的函數。
由于模板允許以泛型(而不是具體類型)的方式來編寫程序,因此有時候也被稱為通用編程,由于類型是用參數表示的,因此模板特性有時也被稱為參數化類型。
建立一個交換模板:

template<typename AnyType> void Swap(AnyType &a, AnyType &b) {AnyType temp;temp = a;a = b;b = temp; }

第一行指出,要建立一個模板,并將類型命名為AnyType,關鍵字template和typename是必需,除非可以使用關鍵字class和typename。另外,必須使用尖括號。類型名可以任意選擇,只要遵循C++的命名規則即可。模板不創建任何函數,而只是告訴編譯器如何定義函數。
將模板放在頭文件中,并在需要使用模板的文件中包含頭文件。
函數模板自動完成重載函數的過程。只需使用泛型和具體算法來定義函數,編譯器將為程序中使用的特定參數類型生成正確的函數定義。

總結

以上是生活随笔為你收集整理的读书笔记||函数探幽的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。