Essential C++濃縮筆記(二)——面向過程的編程風格

來自Stanley B.Lippman的《Essential C++》第二章重要內容的總結,第二章目錄:

2.1 如何編寫函數

2.2 調用函數

2.3 提供默認參數值

2.4 使用局部靜態對象

2.5 聲明inline函數

2.6 提供重載函數

2.7 定義並使用模板函數

2.8 函數指針帶來更大的彈性

2.9 設定頭問題

 

 

一、如何編寫函數 

函數定義包括 返回類型、函數名、參數列表、函數體

 

二、調用函數

函數參數傳遞方式:傳值(把對象的值幅值一份傳入函數);傳址(能改變對象的值)

因爲函數內部定義的對象,只存在與函數執行的期間,一旦函數執行完畢,寫在函數內部區域的內容會被捨棄。對這兩個傳遞方式理解不清楚很容易出錯。

典型的錯誤例子是:

void swap(int val1, int val2)
{
    int temp = val1;        //函數外部定義的變量值不會改變
    val1 = val2;
    val2 = temp;
}

動態內存管理:new表達式的形式爲

new type;

 

三、提供默認參數值

設計函數第二參數默認值,可以根據實際靈活切換函數內部的選擇。在調試輸出日誌文檔時會很方便。

 

四、重載函數

參數列表(參數類型不同 、 參數個數不同)的多個函數,可以有相同的函數名稱

void display_message(char ch);
void display_message(const string&);
void display_message(const string&, int);

但是不同的返回類型無法重載

ostream& display_message( char ch);
bool display_message( char ch);    //無法重載

 

五、模板函數(function template)

可以將函數參數中不同的數據類型用一個框架來表示。

function template以關鍵詞template開場,以成對尖括號<>包圍起來的一個或多個標識符,這些標識符用以表示我們希望推遲決定的數據類型。

template <typename elemtype>        //tpyename名字一定要敲正確!!
void display_message(const string &msg, const vector<elemtye> &vec)
{
    ...
}

另外,模板函數也可以重載

 

六、函數指針帶來更大的彈性

指向函數的指針,所以它的定義方法是

vector<int>* (*seq_ptr)(int);

vector<int>* 是所指函數的返回值,(int)是所指函數的參數列表, (*seq_ptr)中*表示是一個指針變量,seq_ptr是這個變量的名稱。

取函數地址的方法是 函數名稱。

 

七、頭文件

尖括號將頭文件括住的————標準或者項目專屬的頭文件

雙引號將頭文件括住的————用戶提供的頭文件

 

八、程序練習(來自書上的案例的拆解,一定要自己親自實現一遍才能完全理解很多知識點)

Task1:用模板函數編寫顯示函數,能輸出vector<int>、vector<double>、vector<string>等不同數據類型的vector

template <typename elemtype>
void show_vec(vector<elemtype> &vec) {
	if (vec.size() == 0)
		cout << "error:vector is empty!";
	else
	{
		for (int i = 0; i < vec.size(); i++) {
			elemtype t = vec[i];
			cout << t << " ";
		}
		cout << endl;
	}
}

//在申明和定義的時候,都要有template <typename elemtype>,不然編譯器好像不認識elemtype

 

Task2:輸入一個索引,返回Fibonacci數列該索引的元素;能利用靜態對象避免重複計算

#include<iostream>
#include<vector>
#include<string>
using namespace std;

void fibo_elem(int num, vector<int> &vec);
vector<int> fibon_seq(int size);

template <typename elemtype>
void show_vec(vector<elemtype> &);


int main() {
	cout << "please input a number:" << endl;
	int num;
	cin >> num;
	
	vector<int> Fibonacci(num);//vector的定義中一定要說明容器大小
	fibo_elem(num, Fibonacci);		
	//Fibonacci = fibon_seq(num);//靜態vector避免已經計算過的vector重複計算
	show_vec(Fibonacci);


	getchar();
	return 0;
}

void fibo_elem(int num, vector<int> &vec) {
	if (num <= 0 || num >= 1024)
		cout << "error:wrong data";
	else {
		vec[0] = 1;
		vec[1] = 1;
		for (int i = 2; i < num; i++) {
			vec[i] = vec[i - 1] + vec[i - 2];
		}
	}
}

vector<int> fibon_seq(int size) {
	static vector<int> elems;
	if (size > elems.size()){
		for (int ix = elems.size(); ix < size; ix++) {
			if (ix == 0 || ix == 1)
				//elems[ix]=1;
				elems.push_back(1);    //是因爲elems定義的時候沒有說明大小,所以只能由push_back,此時沒有elems[0]的定義
			else
				elems.push_back(elems[ix - 1] + elems[ix - 2]);				
		}
	}	
	return elems;		
}

template <typename elemtype>
void show_vec(vector<elemtype> &vec) {
	if (vec.size() == 0)
		cout << "error:vector is empty!";
	else
	{
		for (int i = 0; i < vec.size(); i++) {
			elemtype t = vec[i];
			cout << t << " ";
		}
		cout << endl;
	}
}

Task3:實現冒泡排序法,理解函數傳值方式與傳址之間的區別

#include<iostream>
#include<vector>
using namespace std;

void swap(int &val1, int &val2);
int a[8] = { 1, 4, 7, 3, 2, 6, 11, 5 };
vector<int> vec(a, a + 8);

template <typename elemtype>		
void show_vec(vector<elemtype> &);
template <typename elemtype>
void bb_sort(vector<elemtype> &vec);

vector<int> fibon_seq(int size);

void main() {
	bb_sort(vec);
	show_vec(vec);	
	getchar();
}

template <typename elemtype>
void bb_sort(vector<elemtype> &vec) {
	for (int i = 0; i < vec.size(); i++) {
		for (int j = i + 1; j < vec.size(); j++) {		
			if (vec[i] > vec[j])
				swap(vec[i], vec[j]);
		}
	}
}

void swap(int &val1, int &val2) {
	int temp;
	temp = val1;
	val1 = val2;
	val2 = temp;
}


template <typename elemtype>
void show_vec(vector<elemtype> &vec) {
	if (vec.size() == 0)
		cout << "error:vector is empty!";
	else
	{
		for (int i = 0; i < vec.size(); i++) {
			elemtype t = vec[i];
			cout << t << " ";
		}
		cout << endl;
	}
}

Task4:利用函數指針,輸入一個索引值,可以返回多個數列(Fibonacci、lucas、Pell、Triangluar)下對應索引下的元素

#include<iostream>
#include<vector>
#include<string>
using namespace std;


vector<int>* fibon_seq(int size);
vector<int>* lucas_seq(int size);

template <typename elemtype>
void show_vec(vector<elemtype> &);

vector<int>* (*vec_ptr)(int);
void fibon_elem(int num, int &elem, vector<int>* (*vec_ptr)(int));

int main() {
	cout << "please input a number:" << endl;
	int num;
	cin >> num;
	
	fibon_elem(num, elem, fibon_seq);		//函數的地址爲函數名稱
	fibon_elem(num, elem, lucas_seq);
	getchar();
	return 0;
}

void fibon_elem(int num,int &elem, vector<int>* (*vec_ptr)(int)) {
	vector<int> *pseq = (*vec_ptr)(num);
	show_vec(*pseq);
	elem = (*pseq)[num - 1];
	cout << elem << endl;
}

vector<int>* fibon_seq(int size) {
	static vector<int> elems;
	if (size > elems.size()) {
		for (int ix = elems.size(); ix < size; ix++) {
			if (ix == 0 || ix == 1)
				elems.push_back(1);			
			else
				elems.push_back(elems[ix - 1] + elems[ix - 2]);
		}
	}
	return &elems;
}

vector<int>* lucas_seq(int size) {
	static vector<int> elems;
	if (size > elems.size()) {
		for (int ix = elems.size(); ix < size; ix++) {
			if (ix == 0)				
				elems.push_back(1);			
			else if (ix == 1)
				elems.push_back(3);
			else
				elems.push_back(elems[ix - 1] + elems[ix - 2]);
		}
	}
	return &elems;		//返回vector的地址效率更高,避免elems對象的複製
}

template <typename elemtype>
void show_vec(vector<elemtype> &vec) {
	if (vec.size() == 0)
		cout << "error:vector is empty!";
	else
	{
		for (int i = 0; i < vec.size(); i++) {
			elemtype t = vec[i];
			cout << t << " ";
		}
		cout << endl;
	}
}

九、編程過程遇到的問題&&總結

1、

vector<int> *Fibonacci;
Fibonacci = fibon_seq(num);
show_vec(*Fibonacci);        //mark!!!!!!!!!!!!!!!!!!!!!!

template <typename elemtype>
void show_vec(vector<elemtype> &vec) {
	if (vec.size() == 0)
		cout << "error:vector is empty!";
	else
	{
		for (int i = 0; i < vec.size(); i++) {
			elemtype t = vec[i];
			cout << t << " ";
		}
		cout << endl;
	}
}

 這個問題涉及對reference的理解,“面對reference的所有操作和麪對’reference所代表的對象‘進行的操作無二”(書P46頁),所以在使用的時候,已經是在面對對象在操作了。所以在調用show_vec的時候要傳入對象,但是實際函數在運行的時候執行的還是reference這種效率高的操作。

仔細讀一讀P46頁的內容!

 

2、vector定義相關

vector<int> vec(size);                 //定義時需要vector的大小

 

static vector<int> elems;           // 如果沒有定義大小,那麼只能通過push_back()往裏面填數據

elems.push_back(1);

 

3、初始化vector<string>

	string str_arr[] = { "wow" ,"hello","ying" };
	vector<string> yy(str_arr, str_arr +3);

 

發佈了31 篇原創文章 · 獲贊 143 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章