1. 程式人生 > >C++ Primer Plus 第8章 函式探幽

C++ Primer Plus 第8章 函式探幽

1.C++行內函數

 行內函數是C++為提高程式執行速度所做的一項改進。常規函式和行內函數之間的主要區別不在於編寫方式,而在於C++編譯器如何將他們組合到程式中。(編譯過程的最終產品是可執行程式------由一組機器語言指令組成),如果程式碼執行時間短,則內聯呼叫就可以節省非內斂呼叫使用的大部分時間。使用行內函數時:(1)在函式宣告前加上關鍵字inline;(2)在函式定義前加上關鍵字inline。

inline工具是C++新增的特性。C語言使用前處理器語句#define來提供巨集------內聯程式碼的原始實現。

2.引用變數

C++新增了一種複合型別:引用變數。引用是已定義的變數的別名(主要用於函式的形參)。建立引用變數如下:

int rats;
int & rodents=rats;//rats和rodents指向相同的值和記憶體單元

      引用和指標的區別:

          (1)必須在宣告引用變數時進行初始化。引用更接近const指標,必須在建立時進行初始化,一旦與某個變數關聯起來,就將一直效忠於它。

int & rodents=rats;//實際上是下面程式碼的偽裝表示
int *const pr=&rats;

     如果只是讓函式使用傳遞給它的資訊,而不對這些資訊進行修改,同時又想使用引用,則應使用常量引用(const....)

    左值引數是可被引用的資料物件,如:變數、陣列元素、結構成員、引用和解除引用的指標都是左值。常規變數屬於可以修改的左值,而const變數屬於不可修改的左值。

     對於形參為const引用的C++函式,如果實參不匹配,則其行為類似於按值傳遞,為確保原始資料不被修改,將使用臨時變數來儲存值。

     C++11新增了另一種引用----右值引用,這種引用可指向右值,是使&&宣告的。

     返回引用的函式實際上是被引用的變數的別名。返回引用時最重要的一點是,應避免返回函式終止時不再存在的記憶體單元引用。

    繼承:能夠將特性從一個類傳遞給另一個類的語言特性。如:ostream是基類,ofstream是派生類,派生類繼承了基類的方法。繼承的另一個特徵是基類引用可以指向派生類物件,而無需進行強制型別轉換。

#include<iostream>
#include<fstream>
#include<cstdlib>
using namespace std;

void file_it(ostream &os, double fo, const double fe[], int n);
const int LIMIT = 5;
int main()
{
	ofstream fout;
	const char *fn = "ep-data.txt";
	fout.open(fn);
	if (!fout.is_open())
	{
		cout << "Can't open" << fn << ".Bye.\n";
		exit{ EXIT_FAILURE };
	}

	double objective;
	cout << "Enter the focal length of your telescope objective in mm:";
	cin >> objective;
	double eps[LIMIT];
	cout << "Enter the focal lengths,in mm,of" << LIMIT << "eyepieces:\n";
	for (int i = 0; i < LIMIT; i++)
	{
		cout << "Eyepieces #" << i + 1 << ":";
		cin >> eps[i];

	}
	file_it(fout, objective, eps, LIMIT);//將目鏡資料寫入到檔案ep_data.txt中
	file_it(cout, objective, eps, LIMIT);//將同樣的資訊以同樣的格式顯示到螢幕上
	cout << "Done\n";
	return 0;
}
void file_it(ostream &os, double fo, const double fe[], int n)
{//引數os(其型別為ostream &)可以指向ostream物件(如cout),也可以指向ofstream物件(如fout)
	ios_base::fmtflags initial;
	initial = os.setf(ios_base::fixed);//方法setf()讓你能夠設定各種格式化狀態。   setf(ios_base::fixed)將物件置於使用定點表示法的模式
	os.precision(0);
	os << "Focal length of objective:" << fo << "mm\n";
	os.setf(ios::showpoint);//setf(ios::showpoint)將物件置於顯示小數點的模式
	os.precision(1);//方法precision指定顯示多少位小數
	os.width(12);//方法width設定下一次輸出操作使用的欄位寬度,這種設定只在顯示下一個值時有效,然後將恢復到預設設定。預設的欄位寬度為零,這意味著剛好能容納下要顯示的內容
	os << "f.1.eyepiece";
	os.width(15);
	os << "magnification" << endl;
	for (int i = 0; i < n; i++)
	{
		os.width(12);
		os << fe[i];
		os.width(15);
		os << int(fo / fe[i] + 0.5) << endl;
	}
	os.setf(initial);
}

   ios_base 是C++標準程式庫中的一個類,定義於標頭檔案中。ios_base類封裝了C++標準中的流輸入輸出中不依賴於讀寫的資料的型別的基本資訊,如格式化資訊、異常狀態、事件回撥函式等。

     格式控制資訊的列舉型別fmtflags ,影響到如何解釋輸入序列的格式、如何生成輸出序列的格式,例如整數是16進位制還是10進製表示,浮點數是科學計數法還是定點形式等;

     使用引用引數的兩個主要原因:(1)能夠修改呼叫函式中的資料物件;(2)通過傳遞引用而不是整個資料物件,可以提高程式的執行速度。

3.預設引數

       對於帶引數列表的函式,必須從右向左新增預設值-------要為某個引數設定預設值,則必須為它右邊的所有引數提供預設值。

4.函式模板

     函式模板允許以任意型別的方式來定義函式。

template<typename AnyType>//建立一個類模板,並將型別命名為AnyType
void Swap(AnyType &a, AnyType &b)
{
    AnyType temp;
    temp=a;
    a=b;
    b=temp;
}

      顯示具體化:提供一個具體化函式定義

struct job
{
    char name[40];
    double salary;
    int floor;
};//如果只交換其中的部分成員,則需使用不同的程式碼。

//C++98標準選擇了下面的方法實現具體化。
//1.對於給定的函式名,可以有非模板函式、模板函式和顯示具體化模板函式以及他們的過載函式。
//2.顯示具體化的原型和定義應以template<>打頭,並通過名稱來指出型別
//3.具體化優先於常規模板,而非模板函式優先於具體化和常規模板。

             過載解析:決定函式呼叫使用哪一個函式定義。

                  (1)完全匹配和最佳匹配:Type表示任意型別-----用作實參的函式名與用作形參的函式指標只要返回型別和引數列表相同,就是匹配的。指向非const資料的指標和引用優先與非const指標和引用引數匹配。

        如果兩個完全匹配的函式都是模板函式,則較具體的模板函式優先。(顯示具體化優先於隱式生成的具體化)。“最具體”並不意味著顯式具體化,而是編譯器推斷使用哪種型別時執行的轉換最少。

//兩個模板
template <class Type> void recycle(Type t);// #1
template <class Type> void recycle(Type * t);// #2
//假設包含模板的程式如下:
strcut blot {int a; char b[10];};
blot ink={25,"spots"};
recycle(&ink);
//recycle(&ink)呼叫與#1模板匹配,匹配時將Type解釋為blot*。recycle(&ink)函式呼叫也與#2模板匹配,這次Type被解釋為ink,recycle<blot *>(blot *)和recycle<blot>(blot *)傳送到可行函式池中,在這兩個模板函式中,recycle<blot *>(blot *)被認為是更具體的,因為在生成過程中,它需要進行的轉換更少。#2模板已經顯示指出,函式引數是指向Type的指標,因此可以直接用blot表示Type;而#1模板將Type作為函式引數,因此Type必須被解釋為指向blot的指標。也就是說,在#2模板中,Type已經被具體化為指標,因此她“更具體”。

          過載解析將尋找最匹配的函式。如果只存在一個這樣的函式,則選擇它;如果存在多個這樣的函式,但其中只有一個是非模板函式,則選擇該函式;如果存在多個合適的函式,且它們都為模板函式,但其中有一個函式比其他函式更具體,則選擇該函式。如果有多個同樣適合的非模板函式或模板函式,但沒有一個函式比其他函式更具體,則函式呼叫將不確定,因此是錯誤的。

#include<iostream>
template<class T>
T lesser(T a,T b)//#1模板函式
{
    return a<b?a:b;
}
int lesser (int a,int b)//#2標準函式
{
    a=a<0?-a:a;
    b=b<0:-b:b;
}
int main()
{
    using namespace std;
    int m=20;
    int n=-30;
    double x=15.5;
    double y=25.9;
    cout<<lesser(m,n)<<endl;
    cout<<lesser(x,y)<<endl;
    cout<<lesser<>(m,n)<<endl;//lesser<>(m,n)中<>指出,編譯器應該選擇模板函式,而不是非模板函式
    cout<<lesser<int>(x,y)<<endl;
    return 0;
}

           關鍵字decltype(C++11新增)

int x;
decltype(x) y;//make y the same type as x
decltype(x+y) xpy;//make xpy the same type as x+y

//decltype(expression) var;//核對表的簡化版如下:
//第一步:如果expression是一個沒有括號括起的識別符號,則var的型別與該識別符號的型別相同,包括const等限定符:
double x=5.5;
double y=7.9;
double &rx=x;
const double *pd;
decltype(x) w;//w is type double
decltype(rx) u=y;//u is type double&
decltype(pd) v;//v is type const double *
//第二步:如果expression是一個函式呼叫,則var的型別與函式的返回型別相同:
long indeed(int)
decltype (indeed(3)) m;//m is type int
//第三步:如果expression是一個左值,則var指向其型別的引用。要進入第三步,expression不能是未用括號括起的識別符號。
double xx=4.4;
decltype ((xx)) r2=xx;//r2 is double &
decltype(xx) w=xx;//w is double(stage 1 match)
//第四步:如果前面的條件都不滿足,則var的型別與expression的型別相同:
int j=3;
int &k=j;
int &n=j
decltype(j+6) i1;//i1 type int
decltype(100L) i2;//i2 type long
decltype(k+n) i3;//i3 type int