您好,登錄后才能下訂單哦!
1、第一部分第八課:傳值引用,文件源頭
2、第一部分第九課預(yù)告:數(shù)組威武,動(dòng)靜合一
這一課的標(biāo)題有點(diǎn)怪。其實(shí)是由這一課的幾個(gè)重點(diǎn)內(nèi)容結(jié)合起來(lái)取的名,慢慢學(xué)習(xí)就知道啦。
上一課《【C++探索之旅】第一部分第七課:函數(shù)效應(yīng),分而治之》中,我們初步認(rèn)識(shí)了函數(shù)。
不過(guò)不要高興得太早,你以為函數(shù)就這樣離你遠(yuǎn)去了嘛?怎么可能,函數(shù)將伴隨一生好嗎,只要你繼續(xù)編程的話。哈哈,所以你是跑不掉了~
【小編,都跟你簽了協(xié)議了,沒(méi)吃藥不要隨便出來(lái)溜達(dá)】
這一課我們就繼續(xù)深入學(xué)習(xí)與函數(shù)相關(guān)的幾個(gè)知識(shí)點(diǎn)。不過(guò)函數(shù)我們會(huì)一直深入學(xué)習(xí)的,以后有了類(lèi),對(duì)象等面向?qū)ο蟮闹R(shí),到時(shí)函數(shù)還會(huì)換一個(gè)稱呼。
值傳遞和引用傳遞
值傳遞
我們首先來(lái)學(xué)習(xí)在函數(shù)范圍內(nèi)操作系統(tǒng)是怎樣管理內(nèi)存的。
再拿我們之前的addTwo函數(shù)舉例。這個(gè)函數(shù)很簡(jiǎn)單,就是在參數(shù)的基礎(chǔ)上加上2,然后返回其值。如下:
int addTwo(int a) { a += 2; return a; }
是不是覺(jué)得 a+= 2; 這一句有點(diǎn)多余呢?完全可以直接 return a + 2; 啊。接下來(lái)會(huì)知道這里為什么添加這一句。
寫(xiě)個(gè)小程序測(cè)試一下此函數(shù):
#include <iostream> using namespace std; int addTwo(int a) { a+=2; return a; } int main() { int number(4), result; result = addTwo(number); cout << "number的值是 : " << number << endl; cout << "調(diào)用函數(shù)后結(jié)果是 : " << result << endl; return 0; }
運(yùn)行這個(gè)程序,輸出:
number的值是 : 4
調(diào)用函數(shù)后結(jié)果是 : 6
程序中最關(guān)鍵的一句當(dāng)然就是
result = addTwo(number);
當(dāng)addTwo函數(shù)被調(diào)用時(shí),其實(shí)發(fā)生了很多事情:
程序獲取number的值,發(fā)現(xiàn)是4。
申請(qǐng)了內(nèi)存中的一塊地址(好像一個(gè)抽屜),抽屜上的標(biāo)簽是a(名字),類(lèi)型是int,抽屜里存放的值等于number的值,為4。
程序進(jìn)入到函數(shù)體中。
將變量a加上2,a變?yōu)?。
a的值被作為函數(shù)的返回值賦給變量result。result的值也變?yōu)?了。
跳出函數(shù)。
重要的一點(diǎn)是:變量number被拷貝到了內(nèi)存的一個(gè)新的地址上(新的抽屜),這個(gè)新的抽屜的標(biāo)簽是a。我們說(shuō)參數(shù)a是通過(guò)值來(lái)傳遞的(number的值拷貝給了a),叫做值傳遞。當(dāng)我們的程序在addTwo函數(shù)里時(shí),內(nèi)存中的情況大致如下圖所示:
因此我們?cè)趦?nèi)存中使用了三個(gè)"抽屜"。注意:number變量并沒(méi)有被改變。
所以在程序中操作變量a的時(shí)候,已經(jīng)與number沒(méi)什么關(guān)系了,只是操作number的一份值的拷貝。
引用傳遞
我們之前的課程初步介紹了引用(reference)的概念。其實(shí)引用這個(gè)名詞太抽象,一般第一次接觸引用的朋友都覺(jué)得:哇,好高深的感覺(jué)。其實(shí)一點(diǎn)也不高深,引用應(yīng)該更確切地被稱為"別名"。
比如小編叫謝恩銘,有的人可能會(huì)戲稱為小銘。那小銘就是我的別名啦,這兩個(gè)名字是不是指同一個(gè)人呢?是,都是指向略微頑皮,但在編程技術(shù)上絕不馬虎的小編。
除了前面說(shuō)的值傳遞的方式,也就是將變量number的值拷貝到變量a中。
除了值傳遞,我們也有其他的方式??梢越o內(nèi)存中名為number的抽屜再貼一個(gè)標(biāo)簽,叫做a。等于給number變量取了一個(gè)別名,稱為a。此時(shí)函數(shù)的參數(shù)就要使用引用了。如下:
int addTwo(int& a) // 注意 & 這個(gè)表示引用的符號(hào) { a+=2; return a; }
當(dāng)我們調(diào)用函數(shù)時(shí),就沒(méi)有之前那樣的值拷貝了。程序只是給了number變量一個(gè)別名而已。當(dāng)我們的程序在addTwo函數(shù)里時(shí),內(nèi)存中的情況大致如下圖所示:
這次,變量a和變量number是指向同一塊內(nèi)存地址(同一個(gè)抽屜),抽屜里存放的值是4,a和number只是這個(gè)抽屜的兩個(gè)不同標(biāo)簽而已。我們說(shuō)變量a是通過(guò)引用傳遞的,稱為引用傳遞。
引用很特別,從上圖中我們看到,我們?cè)趦?nèi)存中并沒(méi)有給a這個(gè)引用變量分配新的內(nèi)存地址,它是指向它所引用的number這個(gè)變量的內(nèi)存地址。所以引用變量和它所指向的變量的內(nèi)存地址是一樣的。我們可以來(lái)測(cè)試一下:
#include <iostream> using namespace std; int main() { int number(4); int &refNumber = number; cout << "number的內(nèi)存地址是 : " << &number << endl; cout << "refNumber的內(nèi)存地址是 : " << &refNumber << endl; return 0; }
運(yùn)行,輸出:
如上圖中所示,變量number和引用變量refNumber的內(nèi)存地址是完全一樣的。
既然值傳遞可以幫我們解決問(wèn)題,為什么要用引用傳遞呢?既生瑜何生亮呢?引用有什么好處呢?
首先,從上例中我們知道了:引用不需要在內(nèi)存中新開(kāi)辟一塊地址,減少開(kāi)銷(xiāo)。
C語(yǔ)言沒(méi)有引用的概念,但是C++有。引用傳遞可以讓我們的函數(shù)addTwo直接修改參數(shù)。繼續(xù)使用之前的測(cè)試程序,只不過(guò)這次在函數(shù)參數(shù)中的是一個(gè)引用:
#include <iostream> using namespace std; int addTwo(int &a) { a+=2; return a; } int main() { int number(4), result; result = addTwo(number); cout << "number的值是 : " << number << endl; cout << "調(diào)用函數(shù)后結(jié)果是 : " << result << endl; return 0; }
運(yùn)行這個(gè)程序,輸出:
number的值是 : 6
調(diào)用函數(shù)后結(jié)果是 : 6
為什么number的值變成了6呢?之前在值傳遞的例子中,number的值是不變的。
很簡(jiǎn)單,我們之前說(shuō)了,引用其實(shí)就是別名。a就是number的一個(gè)別名。那么其實(shí)它們指向的是同一個(gè)內(nèi)存地址,因此對(duì)a做加2操作,也就是對(duì)number做加2操作,當(dāng)然會(huì)改變number的值。
因此,引用的使用要謹(jǐn)慎,因?yàn)樗鼤?huì)改變所引用的那個(gè)對(duì)象。
對(duì)于引用的使用,經(jīng)典的例子也就是swap函數(shù)了,用于交換兩個(gè)參數(shù)的值。
#include <iostream> using namespace std; void swap(double& a, double& b) { double temporary(a); // 將變量a的值保存到變量temporary中 a = b; // 用b的值替換a的值 b = temporary; // 將temporary的值賦給b,就是用a的舊值替換b的值 } int main() { double a(2.3), b(5.4); cout << "a的值是 " << a << " b的值是 " << b << endl; swap(a,b); // 使用swap函數(shù) cout << "a的值是 " << a << " b的值是 " << b << endl; return 0; }
運(yùn)行程序,輸出:
a的值是 2.3 b的值是 5.4
a的值是 5.4 b的值是 2.3
可以看到a和b這兩個(gè)變量的值被交換了。
假如我們不用引用傳遞,而是用傳統(tǒng)的值傳遞,那么我們交換的就只是變量的拷貝而已,并不是變量本身。
暫時(shí),引用的概念對(duì)大家可能還是有些抽象,但是不要擔(dān)心,我們之后的課程會(huì)經(jīng)常用到引用的,關(guān)于引用還有不少要說(shuō)的呢,慢慢來(lái),熟能就生巧了嘛。
之后,學(xué)到指針那章,還會(huì)講解引用和指針的異同。
不可改寫(xiě)的引用傳遞
既然說(shuō)到了引用,就需要介紹一個(gè)引用的慣常用法。這個(gè)用法在接下來(lái)的課程中會(huì)很有用,我們先來(lái)一窺堂奧。
我們說(shuō)引用傳遞相比值傳遞有一個(gè)優(yōu)勢(shì):不進(jìn)行任何拷貝。
設(shè)想一下,如果我有一個(gè)函數(shù),其中一個(gè)參數(shù)是string類(lèi)型的字符串。假如你的這個(gè)字符串變量里面的內(nèi)容是很長(zhǎng)的一串字符串,例如有一本小書(shū)那么多的字
符。那么拷貝這么長(zhǎng)的一個(gè)字符串的開(kāi)銷(xiāo)可是很大很費(fèi)時(shí)的,即使拷貝是在內(nèi)存中進(jìn)行。這樣的拷貝完全沒(méi)什么意義,因此我們就要避免使用值傳遞的方式。
當(dāng)然,你會(huì)自豪地對(duì)我說(shuō):我們可以用引用傳遞啊。是的,好主意。使用引用傳遞的話,就不用拷貝了。但是,引用傳遞有一個(gè)小缺陷:可以修改所指向的對(duì)象。不過(guò)這也不能說(shuō)是缺陷吧,畢竟正是因此引用也才顯得有用啊。
void f1(string text); // 進(jìn)行耗時(shí)的拷貝 { }
void f2(string& text); // 不進(jìn)行拷貝,函數(shù)可以直接修改string變量的值 { }
解決辦法就是使用:不可改寫(xiě)的引用傳遞。
之前的課程我們介紹過(guò)const這個(gè)關(guān)鍵字,被它修飾的變量就變成不可改變的變量。
我們用引用來(lái)避免了拷貝,再用const變量修飾引用就可以使引用不能被修改了。
void f1(string const& text); // 不進(jìn)行拷貝,函數(shù)也不能修改string變量的值 { }
目前來(lái)說(shuō)這個(gè)用法對(duì)我們貌似沒(méi)太大用處,但是本課程的第二部分會(huì)學(xué)習(xí)面向?qū)ο缶幊?,到時(shí)會(huì)經(jīng)常用到這個(gè)技術(shù)。
頭文件和源文件,合理安排
在上一課介紹函數(shù)時(shí),我們已經(jīng)說(shuō)過(guò)函數(shù)是為了可以重用(重復(fù)使用)已經(jīng)創(chuàng)建的"磚塊"(代碼塊)。
目前,我們已經(jīng)學(xué)習(xí)了如何創(chuàng)建自定義的函數(shù),但是這些函數(shù)暫時(shí)還是和main函數(shù)位于同一個(gè)文件中。我們還沒(méi)有真正很好地重用它們。
和C語(yǔ)言一樣,C++也允許我們將程序分割成不同的源文件。在這些源文件中我們可以定義自己的函數(shù)。
在要用到這些函數(shù)時(shí),引入文件的內(nèi)容,也就引入了這些函數(shù)。這樣我們的函數(shù)就可以為不同的項(xiàng)目所使用了。借此我們就可以真正意義上地重用這些被分割開(kāi)的磚塊來(lái)建造房屋了。
必要的文件
但是為了更好地組織程序,計(jì)算機(jī)先驅(qū)們用了兩種文件,而不是一種文件(為何"多此一舉",學(xué)下去就知道了):
源文件(source file):以.cpp結(jié)尾(也可以由.cc,.cxx,.C結(jié)尾),包含函數(shù)的具體源代碼。
頭文件(header file):以.h結(jié)尾(也可以由.hxx,.hpp結(jié)尾),包含函數(shù)的描述,術(shù)語(yǔ)稱為函數(shù)的原型(prototype)。
這下知道為什么今天這課的標(biāo)題里有"文件源頭"了吧?就是指頭文件和源文件。
那么我們還是以addTwo函數(shù)為例,來(lái)分別創(chuàng)建源文件和頭文件吧。
int addTwo(int number) { int value(number + 2); return value; }
我們會(huì)用CodeBlocks這個(gè)IDE(集成開(kāi)發(fā)環(huán)境)來(lái)演示。如果你是用文本編輯器(比如Vim,Emacs,Sublime,等)加gcc(編譯器)來(lái)編寫(xiě)和編譯代碼的,那么直接創(chuàng)建文件就好了。
而且,我們默認(rèn)你的Codeblocks的C++項(xiàng)目已經(jīng)創(chuàng)建好了,也就是說(shuō)已經(jīng)有了一個(gè)自帶main.cpp文件的項(xiàng)目,名稱隨便取。
如下圖:
我們只是演示如何創(chuàng)建函數(shù)的源文件和頭文件。
源文件
首先,我們按照以下順序打開(kāi)菜單欄:File > New > File。
然后在以下窗口中選擇C/C++ source:
然后,點(diǎn)擊Go這個(gè)按鈕。進(jìn)入下圖的窗口:
選擇C++,點(diǎn)擊Next按鈕。進(jìn)入下圖所示窗口:
填寫(xiě)要?jiǎng)?chuàng)建的源文件的完整路徑(選擇目錄,然后填寫(xiě)文件名)。我們把文件的目錄選擇為和main.cpp一樣的目錄。
文件名字盡量做到見(jiàn)名知意,不要來(lái)個(gè) 1.cpp,以后都不知道這文件干什么的。
我們的演示中使用了math.cpp,因?yàn)槲覀兊腶ddTwo函數(shù)進(jìn)行的是數(shù)學(xué)運(yùn)算,就用math(mathematics的縮寫(xiě))來(lái)命名。
然后可以把Debug和Release都選上。
點(diǎn)擊Finish。你的源文件就創(chuàng)建好了。我們接著來(lái)創(chuàng)建頭文件。
頭文件
開(kāi)頭和之前創(chuàng)建源文件是一樣的。依次打開(kāi)菜單:File > New > File,然后在下圖窗口中選擇C/C++ header:
點(diǎn)擊Go進(jìn)入下一步:
建議頭文件的名字和源文件的名字一樣,只是后綴名不同。
頭文件的也放在和main.cpp一樣的目錄。那個(gè)MATH_H_INCLUDED是自動(dòng)生成的,不需要改動(dòng)。
點(diǎn)擊Finish。你的頭文件就創(chuàng)建好了。
一旦源文件和頭文件都創(chuàng)建好之后,你的項(xiàng)目應(yīng)該是類(lèi)似下圖這樣的:
現(xiàn)在文件有了,我們就要往里面填寫(xiě)內(nèi)容了,就是來(lái)定義我們的函數(shù)。
完成源文件
之前說(shuō)過(guò),源文件包含了函數(shù)的具體定義。這是一點(diǎn)。
另外還有一點(diǎn)理解起來(lái)略為復(fù)雜:編譯器需要獲知源文件和頭文件之間存在聯(lián)系。
目前,我們的源文件是空的,我們首先需要往里面添加這樣的一行:
#include "math.h"
你應(yīng)該對(duì)這行代碼的格式不陌生,我們之前說(shuō)過(guò)。
#include <iostream>
這一行我們熟悉的代碼,是為了引入C++的標(biāo)準(zhǔn)iostream(輸入輸出流)庫(kù)。
那么,#include "math.h" 就是為了引入math.h 這個(gè)文件中定義的內(nèi)容。
但是,為什么C++標(biāo)準(zhǔn)庫(kù)的頭文件是包括在尖括號(hào)中,而我們自己定義的math.h是包括在雙引號(hào)中呢?
C++有一些編寫(xiě)好的頭文件(比如標(biāo)準(zhǔn)函數(shù)庫(kù)等等),它們存放在系統(tǒng)的include文件夾里。當(dāng)我們使用#include <文件名> 命令時(shí),編譯器就到這個(gè)文件夾里去找對(duì)應(yīng)的文件。
顯然,用這種寫(xiě)法去包含一個(gè)我們自己編寫(xiě)的頭文件(不在系統(tǒng)include文件夾里)就會(huì)出錯(cuò)了。
所以包含C++提供的頭文件時(shí),應(yīng)該使用尖括號(hào)。
相反地,#include "文件名" 命令則是先在當(dāng)前文件所在的目錄搜索是否有符合的文件,如果沒(méi)有再到系統(tǒng)include文件夾里去找對(duì)應(yīng)的文件。
現(xiàn)在我們就來(lái)完成math.cpp的內(nèi)容,其實(shí)很簡(jiǎn)單,如下:
#include "math.h" int addTwo(int number) { int value(number + 2); return value; }
完成頭文件
我們可以看到,math.h這個(gè)頭文件的初始內(nèi)容不是空的。而是:
#ifndef MATH_H_INCLUDED #define MATH_H_INCLUDED #endif // MATH_H_INCLUDED
這幾句指令又是什么意思呢?
它們是為了防止編譯器多次引入這個(gè)頭文件。編譯器并不總是那么智能的,有可能會(huì)循環(huán)引入同樣的頭文件。
前面說(shuō)過(guò),頭文件包含函數(shù)的原型。我們要把原型寫(xiě)在上面第二句話和第三句話之間,如下所示:
#ifndef MATH_H_INCLUDED #define MATH_H_INCLUDED int addTwo(int number); #endif // MATH_H_INCLUDED
上面的三個(gè)以#開(kāi)頭的語(yǔ)句是如何防止重復(fù)引入頭文件內(nèi)容的呢?
這幾句語(yǔ)句被稱為條件編譯語(yǔ)句。
首先,如果是第一次引入頭文件,那么,就會(huì)讀到第一句命令:
#ifndef MATH_H_INCLUDED
ifndef是if not defined的縮寫(xiě),表示"如果沒(méi)有定義"。整句的意思就是:假如MATH_H_INCLUDED沒(méi)有被定義。
那如果是第一次引入此頭文件,MATH_H_INCLUDED確實(shí)還沒(méi)被定義。#ifndef MATH_H_INCLUDED 成立。
所以就進(jìn)入此條件編譯語(yǔ)句中,執(zhí)行 #define MATH_H_INCLUDED,定義(define是英語(yǔ)"定義"的意思)MATH_H_INCLUDED
然后引入
int addTwo(int number);
最后,來(lái)到第三句條件編譯指令:
#endif // MATH_H_INCLUDED
endif是end if的縮寫(xiě),表示"結(jié)束if條件編譯",后面的 // MATH_H_INCLUDED 是注釋?zhuān)瑫?huì)被編譯器忽略。
然后,假如之后我們又引入此頭文件,編譯器讀到第一句條件編譯指令,
#ifndef MATH_H_INCLUDED
發(fā)現(xiàn)之前已經(jīng)定義過(guò)了 MATH_H_INCLUDED,所以#ifndef MATH_H_INCLUDED不成立。因此不進(jìn)入條件編譯的語(yǔ)句中。直接跳出。就不會(huì)再引入
int addTwo(int number);
了。很妙吧。
當(dāng)然了,我們因?yàn)橛玫氖荂odeBlocks這樣的IDE,它幫你自動(dòng)生成了這三句條件編譯指令。假如我們用的是文本編輯器,那是沒(méi)有這三句自動(dòng)生成的語(yǔ)句的。就需要我們自己手工寫(xiě)了。
MATH_H_INCLUDED可以改成其他任何文本,只需要保證這三句條件指令中這個(gè)文本是一樣的就行,不過(guò)也要保證沒(méi)有兩個(gè)頭文件是使用同樣的文本。
注意。我們以上的函數(shù)原型其實(shí)就是源文件中函數(shù)定義的頭一行,只不過(guò)沒(méi)有了大括號(hào)包括起來(lái)的函數(shù)體,而且在參數(shù)列表的之后多了一個(gè)分號(hào)。
以上演示的是簡(jiǎn)單的情況,是在函數(shù)的參數(shù)是普通變量類(lèi)型時(shí)。假如我們要使用諸如string這樣的類(lèi)型。那么頭文件有些許改動(dòng)哦,如下:
#ifndef MESSAGE_H_INCLUDED #define MESSAGE_H_INCLUDED #include <string> void displayMessage(std::string message); #endif // MESSAGE_H_INCLUDED
可以看到,需要在函數(shù)原型之前加 #include <string>這句指令,表示要引入string這個(gè)C++標(biāo)準(zhǔn)庫(kù)的頭文件。而且,在參數(shù)列表中,string類(lèi)型前也要加上std::
為什么呢?
還記得我們之前在使用cout,cin時(shí),并沒(méi)有再前面加上std:: 嗎?
那是因?yàn)槲覀冊(cè)谑褂胏out,cin前,已經(jīng)用了
using namespace std;
表示使用std命名空間(關(guān)于命名空間,之后的課程會(huì)學(xué)習(xí))。
因?yàn)閏out和cin是位于命名空間std中,因此就可以省略了std::
但是,這里的頭文件中并沒(méi)有 using namespace std; 這句指令(絕對(duì)不建議把using namespace std; 放在頭文件中,要放到源文件中),因此我們需要在string前加上std::,因?yàn)閟tring也位于std命名空間。
現(xiàn)在,我們來(lái)測(cè)試一下addTwo函數(shù)吧。
我 們只需要在main.cpp中加入一行 #include "math.h",表示引入math.h頭文件,而其定義是在math.cpp中,但是因?yàn)閙ath.cpp中已經(jīng)有了#include "math.h",因此我們?cè)趍ain.cpp中就不要指定math.cpp來(lái)引入了。(是否已經(jīng)暈了...)
#include <iostream> #include "math.h" using namespace std; int main() { int a(2), b(3); cout << "a的值是 : " << a << endl; cout << "b的值是 : " << b << endl; b = addTwo(a); // 函數(shù)調(diào)用 cout << "a的值是 : " << a << endl; cout << "b的值是 : " << b << endl; return 0; }
運(yùn)行,顯示:
a的值是 : 2
b的值是 : 3
a的值是 : 2
b的值是 : 4
好了,現(xiàn)在我們真正做到了將可重用的磚塊分開(kāi)存放了。如果之后你想要在另一個(gè)項(xiàng)目中使用addTwo函數(shù),只需要拷貝math.h和math.cpp這兩個(gè)文件過(guò)去就可以了。然后在要使用的源文件中寫(xiě)上 #include "math.h文件所在的路徑/math.h"
我們也可以在同一個(gè)文件中定義多個(gè)函數(shù)。一般來(lái)說(shuō),我們都會(huì)把同一類(lèi)函數(shù)放到同樣的文件中,比如那些數(shù)學(xué)運(yùn)算的函數(shù)放到一個(gè)文件中,那些用于顯示菜單的函數(shù)放到另一個(gè)文件中,等等。
編程,就要有條理,有規(guī)劃。
好的注釋是代碼的一半
最煩看到大型項(xiàng)目中代碼不怎么寫(xiě)注釋的了,特別是那種許多人合作開(kāi)發(fā)的項(xiàng)目,大家的命名規(guī)范和編程風(fēng)格不盡相同,如果沒(méi)有注釋?zhuān)雌饋?lái)相當(dāng)累。
一般不寫(xiě)注釋的程序員不是好程序員,只顧自己寫(xiě)代碼寫(xiě)得爽,完全不關(guān)心后來(lái)人是不是被他的代碼拍死在沙灘上。應(yīng)該被拖出去高速揉臉五分鐘(這是什么呆萌的刑罰,那畫(huà)面太美我不敢看):
寫(xiě)注釋對(duì)于函數(shù)特別有用。因?yàn)槟愫芸赡軙?huì)用到別的程序員寫(xiě)的函數(shù)。那么如果已經(jīng)有了對(duì)函數(shù)的作用等的注釋?zhuān)阋话憔筒恍枰x函數(shù)的所有代碼了(程序員是會(huì)"偷懶"的)。
對(duì)于一個(gè)函數(shù),一般我們的注釋要包含三方面:
函數(shù)的功用
參數(shù)列表
返回值
我們就來(lái)給addTwo函數(shù)寫(xiě)個(gè)注釋吧:
#ifndef MATH_H_INCLUDED #define MATH_H_INCLUDED /* * 對(duì)參數(shù)進(jìn)行加2操作 * - number : 要進(jìn)行加2操作的數(shù) * 返回值 : number + 2 */ int addTwo(int number); #endif // MATH_H_INCLUDED
寫(xiě)注釋的格式隨個(gè)人愛(ài)好不盡相同。不顧一般常用的注釋格式是這樣(doxygen的格式):
/** * \brief 對(duì)參數(shù)進(jìn)行加2操作 * \param number 要進(jìn)行加2操作的數(shù) * \return number + 2 */ int addTwo(int number);
參數(shù)的默認(rèn)值
函數(shù)的參數(shù),我們已經(jīng)學(xué)習(xí)過(guò)了。如果一個(gè)函數(shù)有三個(gè)參數(shù),那么我們需要向函數(shù)提供這三個(gè)參數(shù)的值,才能讓函數(shù)被正常調(diào)用。
但是,也并不一定。我們用以下函數(shù)來(lái)學(xué)習(xí)一下默認(rèn)函數(shù)參數(shù)的用法:
int secondNumber(int hours, int minutes, int seconds) { int total = 0; total = hours * 60 * 60; total += minutes * 60; total += seconds; return total; }
這個(gè)函數(shù)的作用就是根據(jù)給出的小時(shí)數(shù),分鐘數(shù),秒數(shù),計(jì)算出一共有多少秒。非常好理解。
因此,我們說(shuō)hours, minutes, seconds是函數(shù)secondNumber的三個(gè)參數(shù)。在函數(shù)被調(diào)用時(shí),我們須要給它這三個(gè)參數(shù)一定的值。
這個(gè)我們之前早就學(xué)過(guò)了。
參數(shù)默認(rèn)值
我們要學(xué)習(xí)新知識(shí)點(diǎn),就是我們其實(shí)可以給函數(shù)的參數(shù)指定默認(rèn)值。假如函數(shù)在被調(diào)用時(shí)這些參數(shù)沒(méi)有被指定值,那么就用默認(rèn)的值。
首先來(lái)看沒(méi)有指定默認(rèn)值的情況:
#include <iostream> using namespace std; // 函數(shù)原型 int secondNumber(int hours, int minutes, int seconds); // 主函數(shù) int main() { cout << secondNumber(1, 10, 25) << endl; return 0; } // 函數(shù)定義 int secondNumber(int hours, int minutes, int seconds) { int total = 0; total = hours * 60 * 60; total += minutes * 60; total += seconds; return total; }
運(yùn)行,顯示:
4225
因?yàn)?1小時(shí)等于3600秒,10分鐘等于600秒,25秒等于... 25秒。所以 3600 + 600 + 25 = 4225
現(xiàn)在,假如我們要將secondNumber函數(shù)的部分參數(shù)設(shè)為有默認(rèn)值的參數(shù)。例如,我指定分鐘數(shù)minutes和seconds默認(rèn)都為0,因?yàn)橐话阄覀兌加谜c(diǎn)比較多,就只有小時(shí)數(shù)。
我們須要改寫(xiě)函數(shù)的原型,如下所示:
int secondNumber(int hours, int minutes = 0, int seconds = 0);
看到了嗎?在minutes和seconds后面,都多了
= 0
這個(gè)就是函數(shù)的默認(rèn)參數(shù)值。此處都設(shè)為0。測(cè)試如下:
#include <iostream> using namespace std; // 函數(shù)原型 int secondNumber(int hours, int minutes = 0, int seconds = 0); // 主函數(shù) int main() { cout << secondNumber(1) << endl; return 0; } // 函數(shù)定義,沒(méi)有寫(xiě)參數(shù)默認(rèn)值 int secondNumber(int hours, int minutes, int seconds) { int total = 0; total = hours * 60 * 60; total += minutes * 60; total += seconds; return total; }
運(yùn)行,顯示:
3600
這是因?yàn)閔ours為1,而minutes和seconds沒(méi)指定,默認(rèn)為0。1小時(shí)等于3600秒。
請(qǐng)注意:函數(shù)參數(shù)的默認(rèn)值只能寫(xiě)在函數(shù)的原型中,假如寫(xiě)在函數(shù)的定義中,編譯器會(huì)報(bào)錯(cuò)。
假如改為:
cout << secondNumber(1,10) << endl;
則輸出:
4200
因?yàn)閔ours為1;minutes為10;seconds沒(méi)指定,默認(rèn)為0
所以3600 + 600 = 4200
特殊情況
函數(shù)的默認(rèn)參數(shù)的使用中,會(huì)出現(xiàn)多種情況,有些比較特殊,我們舉例如下(還是以secondNumber函數(shù)為例):
1.假如我指定了hours和seconds的值,但是minutes的值沒(méi)指定,那么如何做呢?
你不可以使用這樣的形式:
cout << secondNumber(1,,25) << endl;
這樣是會(huì)出錯(cuò)的。在C++中,我們不能跳過(guò)參數(shù),即使它有默認(rèn)值。如果我們給開(kāi)頭和結(jié)尾的參數(shù)賦了值,那么中間的參數(shù)也需要賦值。
還是得這么寫(xiě):
cout << secondNumber(1, 0, 25) << endl;
2.這樣寫(xiě)可以嗎?
int secondNumber(int hours = 0, int minutes, int seconds);
不可以,會(huì)出錯(cuò)。因?yàn)閰?shù)的默認(rèn)值必須要靠右寫(xiě)。也就是說(shuō),假如只有hours這個(gè)參數(shù)有默認(rèn)值,那么必須要把hours參數(shù)寫(xiě)到最右邊,如下所示:
int secondNumber(int minutes, int seconds, int hours = 0);
這樣就沒(méi)有錯(cuò)誤了。
3.我能否將所有參數(shù)都設(shè)為有默認(rèn)值?
可以。舉例如下:
int secondNumber(int hours = 0, int minutes = 0, int seconds = 0); cout << secondNumber() << endl;
輸出就是0了。
設(shè)置默認(rèn)參數(shù)的兩條主要規(guī)則
只有函數(shù)原型才能包含參數(shù)的默認(rèn)值。
參數(shù)的默認(rèn)值是寫(xiě)在參數(shù)列表之后。而且是靠右邊寫(xiě)。
總結(jié)
一個(gè)函數(shù)可以接收數(shù)據(jù)(通過(guò)參數(shù)),也可以返回?cái)?shù)據(jù)(通過(guò)retur函數(shù)可以接收引用作為參數(shù),以直接修改內(nèi)存中的信息。
當(dāng)你的程序漸漸多起來(lái)時(shí),建議將其分為不同的文件。每個(gè)文件中存放特定的函數(shù),而且文件是成對(duì)組織的:.cpp文件存放函數(shù)的定義,.h文件存放函數(shù)的原型。
一定要寫(xiě)好注釋。
今天的課就到這里,一起加油吧!
下一課我們學(xué)習(xí):數(shù)組威武,動(dòng)靜合一
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。