天天看點

C++ Primer學習之(7)——函數

P307:

函數調用做了兩件事情:用對應的實參初始化函數的形參,并将控制權轉移給被調用函數。主調函數的執行被挂起,被調函數開始執行。函數的運作以形參的(隐式)定義和初始化開始。

P308:

函數不能傳回另一個函數或者内置數組類型,但可以傳回指向函數的指針,或指向數組元素的指針的指針。

注:C++還是很強大的。

P314:

複制實參的局限性

複制實參不适宜的情況:

1. 當需要再函數中修改實參的值時;

2. 當需要以大型對象作為實參傳遞是。複制對象所付出的時間和存儲空間代價往往過大;

3. 當沒有辦法實作對象的複制時。

有效的解決辦法是将形參定義為引用或指針類型。

注:啥時候我也能靠分析而不是死記侃侃而談呀。

P316:

從C語言背景轉到C++的程式員習慣通過傳遞指針來實作對實參的通路。在C++中,使用引用形參則更安全和更自然。

注:看來我的想法要從C轉變到C++才可以。

P316:

使用引用形參傳回額外的資訊。

一個例子:

// returns an iterator that refers to the first occurance of value
// the reference parameter occurs contains a second return value
vector<int>::const_iterator find_val(vector<int>::const_iterator beg, //first element
				     vector<int>::const_iterator end, // last element
				     int value, // the value we want
				     vector<int>::size_type &occurs) // number of time it occurs
{
	// res_iter will hold first occurence, if any 
	vector<int>::const_iterator res_iter = end;
	occurs = 0; // set occurence count parameter
	for (; beg != end; ++beg)
	{
		if (*beg == value)
		{
			// remember first occurrence of value
			if (res_iter == end)
			{
				res_iter = beg;
			}
			++occurs; // increment occurence count
		}
	}
	return res_iter; // count returned implicitly in occurs
}
           

可如此調用:

<span style="font-family:Microsoft YaHei;">it = find_val(ivec.begin(), ivec.end(), 42, ctr);</span>
           

注:差距。如果要我實作功能類似的函數,一定是一大堆數組,一大堆指針,首先思維要扭轉過來。

P318:

*更靈活的指向const的引用

應該将不修改相應實參的形參定義為const引用。例如:

// returns index of first occurence of c in s or s.size() if c isn't in s
// Note: s doesn't change, so it should be a reference to const
string::size_type find_char(string &s, char c)
{
    string::size_type i = 0;
    while(i != s.size() && s[i] != c)
        ++i; // not found, look at next character
    return i;
}
           

這樣定義的問題是不能通過字元串字面值來調用這個函數:

if (find_char("Hello World", 'o')) //...
           

上述調用會導緻編譯失敗。

即使程式本身沒有const對象,而隻使用string對象調用find_char函數,編譯階段的問題依然會出現,例如,另一個函數is_sentence調用find_char來判斷一個string對象是否是句子:

bool is_sentence(const string &s)
{
    // if there's a period and it's the last character in s
    // then s is a sentence
    return (find_char(s, '.') == s.size() -1);
}
           

傳遞進is_sentence的形參是指向const_string對象的引用,不能講這種類型的參數傳遞給find_char,因為後者期待得到一個指向非const string對象的引用。

應該将不需要修改的引用形參定義為const引用。普遍的非const引用形參在使用時不太靈活。這樣的形參既不能用const對象初始化,也不能用字面值或産生右值的表達式實參初始化。

注:之前編碼的時候出現過好多次類似的編譯錯誤,都沒有太放在心上,好像擺弄一下就能編譯通過,其實已經在代碼中埋下了隐患,不是合理的設計。

P322:

*vector和其他容器類型的形參

通常,函數不應該有vectro或其他标準庫容器類型的形參。調用含有普通的非引用vector形參的函數将會複制vector的每一個元素。

從避免複制vector的角度出發,應考慮将形參聲明為引用類型。事實上,C++程式員傾向于通過傳遞指向容器中需要處理的元素的疊代器來傳遞容器:

// pass iterators to the first and one past the last element to print
void print(vector<int>::const_iterator beg,
	vector<int>::const_iterator end)
{
	while (beg != end)
	{
		cout << *beg++;
		if (beg != end)
		{
			cout << " "; // no space after last element
		}
	}
	cout << endl;
}
           

注:很好的經驗。

P326:

*matrix兩邊的園括号是必需的:

int *matrix[10]; // array of 10 pointers
int (*matrix)[10]; // pointer to an array of 10 ints
           

P327:

有三種常見的程式設計技巧確定函數的操作不超出數組實參的邊界:

1. 在數組本身放置一個标記來檢測數組的結束。處理C風格字元串的程式就是使用這個标記停止數組元素的處理(NULL)。

2. 傳遞指向數組第一個和最後一個元素的下一個位置的指針。這種程式設計風格由标準庫所使用的技術啟發而得。

3. 将第二個形參定義為表示數組的大小,這種用法在C程式和标準化C++程式中十分普遍。

P329:

int main(int argc, char *argv[]) { ... }
           

第二個形參argv是一個C風格字元串數組。第一個形參argc則用于傳遞該數組中字元串的個數。

P330:

含有可變形參的函數

注:沒看懂,因為之前沒有用過可變形參,從來沒有弄明白過。

http://punch.blog.163.com/blog/static/2285731201092894021668/,這篇還不錯,弄明白一點點了。

P333:

主函數main的傳回值:

cstdlib檔案頭檔案定義了兩個預處理變量,分别用于表示程式運作成功和失敗:

#include <cstdlib>
int main()
{
    if (some_failure)
    {
         return EXIT_FAILURE;
    }
    else
    {
        return EXIT_SUCCESS;
    }
}
           

P338:

遞歸函數必須定義一個終止條件;否則,函數就會“永遠”遞歸下去,這意味着函數會一直調用自身直到程式棧耗盡。有時候,這種現象稱為“無限遞歸錯誤”。

注:有一次面試的時候就忘記了寫遞歸終止條件。

P339

函數原型為定義函數的程式員和使用函數的程式員之間提供了接口。在使用函數時,程式員隻對函數原型程式設計即可。

把函數聲明直接放到每個使用該函數的源檔案中,這可能是大家希望的方式,而且也是合法的。但問題在于這種用法比較呆闆而且容易出錯。解決的方法是把函數聲明放在頭檔案中,這樣可以確定對于指定函數其所有聲明保持一緻。

注:接口,接口,這個出現頻繁的詞,原來是這個意思。

P343:

既可以在函數聲明也可以在函數定義中指定預設實參。但是,在一個檔案中,隻能為一個形參指定預設實參一次。

通常,應在函數聲明中指定預設實參,并将該聲明放在合适的頭檔案中。

注:預設實參的正确用法。

P345:

一個變量如果位于函數的作用域内,但生命期跨越了這個函數的多次調用,則應該将這樣的對象定義為static(靜态的)。

注:我喜歡靜态變量,看起來很cool。

P347:

inline函數避免函數調用的開銷

把inline函數放在頭檔案,確定在調用函數時所使用的定義時相同的,并且保證在調用點該函數的定義對編譯器課件。

P350:

this指針的引入

每個成員函數(static成員函數除外)都有一個額外的,隐含的形參this。

total.same_isbn(trans);
           

編譯器會這樣重寫這個函數調用:

// pseudo-code illustration of how a call to a member function is translated
Sales_item::same_isbn(&total, trans);
           

P351:

const成員函數的引用

跟在成員函數聲明的形參表後面的const所起的作用:const改變了隐含的this形參的類型。

用這種方式使用const的函數稱為常量成員函數。由于this指向const對象的指針,const成員函數不能修改調用該函數的對象,隻能讀取而不能修改。

注:soga,之前一直沒有弄明白跟在成員函數後面的const的含義。

P335:

合成的預設構造函數一般适用于包含類類型成員的類。而對于含有内置類型或複合類型成員的類,則通常應該定義他們自己的預設構造函數初始化這些成員。

P358:

如果兩個函數聲明的傳回類型和形參表完全比對,則将第二個函數聲明視為第一個的重複聲明。如果兩個函數的形參表完全相同,但傳回類型不同,則第二個聲明是錯誤的:

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

函數不能僅僅基于不同的傳回類型而實作重載。

P361:

在C++中,名字查找發生在類型檢查之前。

P365:

在實際應用中,調用重載函數時應盡量避免對實參做強制類型轉換:需要使用強制類型轉換意味着所設計的形參集合不合理。

注:平時使用中沒有注意到的,在使用GDI+相關調用時,為了讓編譯通過,經常使用強轉,需要改掉這個壞習慣。重載這一節講的比較多,沒有太仔細看,如果以後有不明白的地方再回來好好看看重載的分析吧。

P370:

指向函數的指針

// pf points to function returning bool that takes two const string references
bool (*pf)(const string&, const string&);
           

*pf兩側的圓括号是必須的:

// declares a function named pf that returns a bool*
bool *pf(const string &, const string &);
           

用typedef簡化函數指針的定義

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

cmpFcn是一種指向函數的指針類型的名字。要使用這種函數指針類型時,隻需直接使用cmpFcn即可,不必每次都把整個類型聲明全部寫出來。

注:第七章還有一小部分沒有看完,但是有點累了,休息了。——201406081800

P373

函數指針形參

/* useBigger function's third parameter is a pointer to function
 * that function returns a bool and takes two const string references
 * two ways to specify that parameter:
 */
// third parameter is a function type and is automatically treated as a pointer to function
void useBigger(const string &, const string &,
			   bool(const string &, const string &));
// equivalent declaration: explicitly define the parameter as a pointer to function
void useBigger(const string &, const string &,
			   bool (*)(const string &, const string &));
           

第七章 函數(完)

繼續閱讀