來自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);