#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;
}