c++ primer(第五版)筆記 第六章 函數(2)

#ifndef FUNC_H
#define FUNC_H

#include<iostream>
#include<string>
#include<vector>
#include<cstdlib>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::vector;

char &get_val(string &s, string::size_type pos);

//可以將小型函數定義爲內聯函數(inline function),可以減少函數調用的開銷
// inline 只是向編譯器請求,編譯器可以選擇忽略。。。
//如果要將 inline 的聲明和定義分開文件,需要注意
/*
csdn 論壇:http://bbs.csdn.net/topics/100081253
我概括過inline函數的一條基本原則(不知道別人有沒有類似提法):

首先介紹一個概念,“編譯單元”,用不太嚴謹的方式定義,就是
當你把一個源文件(.cpp .cxx等)做完預處理,也就是把包含的頭文件的內容全部放
到這個文件裏來,所有宏都展開,等等,形成的一個邏輯上的實體——就是編譯單元
一個編譯單元可以單獨編譯,但不能鏈接成一個可執行程序(除非程序只有這個編譯單元)

有了編譯單元的概念以後,你只要確保以下這個原則就可以了:
如果在這個編譯單元裏使用了一個Inline函數,那麼我在這個編譯單元結束之前,
必須能夠“看到”這個編譯單元的完整定義(所有實現代碼)

另外要注意,在編譯單元之內,調用inline函數的代碼行之前,至少要放置
一個這個inline函數的聲明,當然有定義也可以

從這個原則出發,最簡單的使用Inline函數的方法就是在頭文件定義,
否則你要在每一個使用inline函數的編譯單元裏一一定義這個函數,
如果有n個編譯單元,你就要把inline函數的代碼重複書寫n次
*/

//返回列表
inline vector<int> get_vector()
{
	return{3,4,52};
}

//定義返回數組指針的函數
//1.定義類型別名
typedef int parr[5]; 
//using parr = int[5];
extern int arr1[5];
extern int arr2[5];
parr *func1(int);

//2. 不使用類型別名
//Type ( *function( parameter_list)) [ demension]
//Type 數組元素的類型, demension 數組的維度, function 函數名
int ( *func2( int))[ 5];	//func1 不使用類型別名

//3.使用尾置返回類型(trailing return type)
auto func3(int i) -> int(*)[5];

//4.使用已知數組和 decltype 關鍵字
decltype(arr1) *func4(int i);
decltype(arr1) &func5(int i);

//重載函數(overloaded):同一個作用域內函數名相同,形參列表(形參數量和類型)不同
//main函數不能重載
//不允許兩個函數除返回類型以外的其他要素都相同
//頂層 const 形參和無頂層 const 的形參沒有區別,拷貝初始化時忽略頂層 const
//底層 const 指針或引用,通過區分指向對象是常量還是非常量來實現函數重載

//const_cast 強制類型轉換
const string &shorter(const string &s1, const string &s2);
//如果需要返回非常量
string &shorter(string &s1, string &s2);

//默認實參(default argument),當爲一個形參賦予了默認值,則其後面的所有形參都必須有默認值
		// 調用時,如果想覆蓋某位置的默認實參,則必須爲該位置之前的所有形參賦予實參
//一般在頭文件中提供默認實參,並且在同一個作用域內,一個形參只能被賦值一次
//函數匹配(function matching)
//重載確定(overload resolution)
// 1.選定本次調用的重載函數集,候選函數(candidate function)
	// 規則:1>同名 2.聲明在調用點可用
// 2.考察實參,可行函數(viable function),如果找不到報告無匹配函數的錯誤
	// 規則:1>形參數量和實參數量一致 2>實參的類型和對應形參的類型一致
// 3.最佳匹配
	// 1>精確匹配
		// 形參和實參類型相同
		// 實參從數組類型或函數類型轉換成對應的指針類型
		// 頂層 cosnt 
	// 2.底層 const 轉換
	// 3.類型提升
	// 4.算術類型轉換
	// 5.類類型轉換
	
//三種調用結果:
//1>編譯器找到一個與實參最佳匹配(best match)的函數,生產調用該函數的代碼
//2>找不到與實參匹配(no match)的函數,編譯器報無匹配的錯誤
//3>有多於一個函數可以匹配,但都不是最佳匹配,報二義性調用(ambiguous call)錯誤

//函數指針
//由返回類型和形參列表共同決定
//不同類型的函數指針不存在轉換規則,但可以用 nullptr 或 0 的整型常量表達式賦值
//func1 的指針, 可以直接使用 pfunc1 調用函數
extern parr *(*pfunc1)(int);

//重載函數的指針,指針類型必須與重載函數的其中一個精確匹配
string &(*pShorter)(string &s1, string &s2);	//未定義

#endif	//FUNC_H

#include "func.h"

char &get_val(string &s, string::size_type pos)
{
	//retrun 終止當前正在執行的函數並將控制權返回該函數調用的地方
	//兩種形式: 
		//return;
			//只用於返回類型是 void 的函數中,也可以使用 returned expression 形式,但 expression 必須是一個返回 void 的函數
		//returned expression
			//只要函數的返回類型不是一個 void,則函數內的每條 return 語句必須返回一個值,該值的類型必須和函數的返回類型一致,或者能隱式轉換
			//函數完成後,所佔用的存儲空間也隨之被釋放掉,函數終止意味着局部變量的引用和指針不再指向有效的內存區域
	return s[pos];	//pos 合法
}

int arr1[5]{1, 2, 3, 4, 5};
int arr2[5]{6, 7, 8, 9, 0};

//1.類型別名
parr *func1(int i)
{
	return (i % 2) ? &arr1 : &arr2;
}
//2.不使用類型別名
int(*func2(int i))[5]
{
	return (i % 2) ? &arr1 : &arr2;
}
//3.使用尾置返回類型 c++11
auto func3(int i) -> int(*)[5]
{
	return (i % 2) ? &arr1 : &arr2;
}
//4.使用已知數組和 decltype 關鍵字
decltype(arr1) *func4(int i)
{
	return (i % 2) ? &arr1 : &arr2;
}
//將 func4 改成返回引用
decltype(arr1) &func5(int i)
{
	return (i % 2) ? arr1 : arr2;
}

//const_cast 強制類型轉換
const string &shorter(const string &s1, const string &s2)
{
	return s1.size() <= s2.size() ? s1 : s2;
}
//如果需要返回非常量的重載函數
string &shorter(string &s1, string &s2)
{
	auto &r = shorter(const_cast<const string&>(s1), const_cast<const string&>(s2));
	return const_cast<string&>(r);
}

//constexpr 函數,能用於常量表達式0
//所有的形參類型和返回類型均爲字面值類型,函數體中有且只有一條 return 語句
//隱式定義爲 inline 
//允許返回值是一個非常量
constexpr int cexpr1(){ return 21;}
constexpr int cexpr2(const int n){ return cexpr1() * n;}	
extern int arr3[cexpr2(2)];
 int m = 1;
// extern int arr4[cexpr2(m)];	//cexpr2 返回非常量

parr *(*pfunc1)(int) = func1;	//取地址符可選 parr *(*pfunc1)(int) = &func1

#include "func.h"

int main()//main函數不能重載
{
	string s{"hello world"};
	vector<int> vi;

	get_val(s, 3) = 'M';	//調用返回引用的函數得到左值
	cout <<"s : "<< s << endl;
	
	vi = get_vector();
	for (auto i : vi)
		cout << i << endl;


	parr *p1 = func1(2);
	int (*p2)[5] = func2(3);
	int (*p3)[5] = func3(5);
	int (*p4)[5] = func4(2);
	int (*p5)[5] = pfunc1(2);	//直接使用 pfunc1 調用函數,解引用符可選(*pfunc1)(2)
	for (size_t i = 0; i != 5; ++i)
	{
		cout << "P1: " << (*p1)[i] << endl;
		cout << "P2: " << (*p2)[i] << endl;
		cout << "P3: " << (*p3)[i] << endl;
		cout << "P4: " << (*p4)[i] << endl;
		cout << "P5: " << (*p5)[i] << endl;
	}
	func5(2)[3] = 6234;	//返回引用類型可作爲左值
	for (size_t i = 0; i != 5; ++i)
	{
		cout << "arr2: " << arr2[i] << endl;
	}
	
	// const_cast 和 重載
	string s1 = "hello";
	string s2 = "world!";
	// shorter("hello", "world!")[2] = 'H';	//調用const 形參版本,返回指向 const 對象的引用,不可以修改
	shorter(s1, s2)[2] = 'H';	//調用非const 形參版本,返回非 const 引用,如果未定義非 const 形參版本,則返回 const 引用
	cout << s1 << endl;
	
	//允許 main 函數沒有 return,編譯器隱式插入一條返回 return 0 的語句
	//0 表示執行成功,非 0 值具體含義由機器定義。cstdlib 定義了兩個預處理變量 EXIT_FAILURE, EXIT_SUCCESS
	return EXIT_SUCCESS;
}



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