該博客用於記錄自己在閱讀過程中不懂的知識點,很少用到但比較重要的知識點以及模棱兩可的知識點
只存在與函數執行期間打對象叫自動對象,與之對應的是局部靜態對象
傳參時可以只用引用避免拷貝,拷貝大的對象效率低而且有些類類型不支持拷貝,使用引用還可以返回額外信息
實參初始化形參時會忽略頂層const,這就意味着
void func(const int i);
void func(int i);
並不是重載函數,會報出重複定義動錯誤,第一個func既可以傳入constint也可以傳入 int但func並不能向i寫值。
形參的初始化和變量的初始化是一樣的
voidprint(const int[10]); //這裏的10只是我們的期望值,實際上不一定,因爲傳入的實際上是指針爲了限制數組的規格,我們一般採用3
種方法:
1.使用標記//’\0’
2.使用標準庫規範//begin和end指針
3.顯式的傳入一個表示數組規格的形參
如果數組不需要寫操作,我們一般把數組形參寫成指向const的指針
我們也可以把形參寫成數組動引用,此時需要帶上數組動規格, 例:
void print(int (&arr)[10]){
/*
*
*/
}
但這樣寫無疑是限制例我們只能將函數作用於大小爲10的數組
二維數組的形參定義
void print(int ( *matrix) [10] ){}
void print(int matrix[][10]){} //等價
main函數處理命令行選項 :
aegc是一個int型變量用於表示argv字符數組中的字符串數量,舉個例子幫助理解
prog-d -o ofile data0
argv[0]= “prog”;
argv[1]= “-d”;
argv[2]= “-o”;
argv[3]= “ofile”;
argv[4[= “data0”;
argv[5]= 0;
含有可變形參的函數
形參類型相同時:initializer_list與vector類似,不一樣的是這個對象中的元素永遠是常量
.size()表示對象中的元素數量
.begin()和.end()首尾指針
值得一提的是initializer_list對象之間賦值時不會拷貝列表中的元素/*list2(list)*/ /*list2=lsit*/,拷貝完成後原始列表和副本共享元素
傳參時不僅可以傳initializer_list對象也可以傳一對花括號包裹的值序列
當形參類型不同時我們可以編寫一種特殊的函數,也就是所謂的可變參數模板(後面再提)
省略符形參是爲了方便C++程序訪問某些特殊的C代碼而設定的,這些代碼使用了名爲varargs的C標準庫功能。通常省略符形參不應
該用與其他目地。
函數返回一個值的方式和初始化一個變量或形參的方式完全一樣
返回類型爲引用的函數得到左值,其他類型得到右值,可以像正常左值一樣使用返回函數的引用的調用
/*func(a).size */ //func是一個返回非常量引用的函數
也可以對返回非常量引用的函數的結果賦值
/*func(a) = “asda” */
函數也可以返回花括號包裹的值列表
vector <string> func(){
return{“a”, “bb”, “ccc”};
}
main函數的返回值並不是void,返回0表示執行成功,如果結尾沒有return語句,編譯器會隱式的插入一條return0;返回非0值表示執行失
敗,具體含義由機器決定,爲了使返回值與機器無關。cstdlib定義了兩個預處理變量(由預處理器定義,不能加std,也不能加using)
return EXIT_FAILURE;
return EXIT_SUCCESS;
返回一個數組的指針或引用的函數比較繁瑣,常用別名來簡化操作
typedef int arr[10];
using arr = int[10]; //arr是 類型爲10個整數的數組的別名
arr* func(int i); //返回值是一個指向含有10個整數的數組的指針
不用別名聲明一個返回數組指針的函數
int (*p)[10] = &arr; //p2是一個指針,它指向含有10個整數的數組
我們可以從上面得到啓發,不使用別名:
int ( *func ( int i) ) [10];
使用尾置返回類型依然可以簡化上述操作
auto func(int I) -> int(*)[10];
也可以使用decltype
int odd[] = {1, 3, 5, 7, 9}; //odd是一個含有5個整型元素的數組
decltype(odd) *func(int I);
函數重載時頂層const的有無是等價的,會導致重複定義
int func(int i);
int func(const int i);
int func(int *i);
int func(int *const I);
底層const可以構成重載
int func(int &i);
int func(const int &i);
int func(int *i);
int func(const int *i);
const_cast和重載,填前面的坑
const string &shortStr(cosnt string &s1, const string &s2){
returns1.size() <= s2.size() ? s1 : s2;
}
然而雖然該函數可以傳進 常量和非常量 引用,但它返回的結果是常量引用
所以我們可以寫一個重載函數將形參定爲非常量引用,再用一個常量引用的副本接受上面那個函數的返回值(其中形參可以通過
const_cast轉換爲常量引用),然後在返回的時候再轉換爲非常量引用
string& shortStr(string &s1, string &s2){
auto &r = shortStr(const_cast<const string&> (s1),const_cast<const string&>(s2));
return const_cast<string &>(r);
}
當一個函數多次聲明時,後續聲明不能修改前面聲明的默認實參,並且要保證右邊的形參全部都有默認實參
constexpr函數可以作用於常量表達式,但其返回值和形參都必須是字面值類型且只有一個return語句,概括的說cosntexpr函數在編譯期
間就必須能夠知道函數的所有信息,constexpr函數被隱式的指定爲內聯函數,因爲在編譯的時候編譯器就要將內聯函數展開,所有內
聯函數的聲明和定義一般都寫在頭文件中
爲了幫助調試程序,產生例許多幫助調試的東西
1.assert(expr)這是一種預處理宏,由預處理器管理,如果expr表達式爲0則終止程序執行,如果爲假則什麼也不做。和其他預處理變量
一樣,使用它不需要std::和using
assert預處理宏的執行依賴於一NDEBUG預處理變量的定義,如果定義了NDEBUG,則表示關閉例調試狀態,無論expr表達式是什麼
assert都不執行
因爲NDEBUG的定義用到的是#define,所以,我們可以靈活使用它,而不僅僅侷限於使用assert函數,比如我們可以像條件編譯一樣
使用它:
#ifndef NDEBUG
/*
*此處可以寫自己的調試代碼
*/
#endif
編譯器還定義了一個__func__變量用於表示當前函數的名字,除此之外預處理器也定義了4個預處理變量:
__FILE__存放文件名的字符串字面值;
__LINE__存放當前行號的整型字面值;
__TIME__存放文件編譯的時間的字符串字面值
__DATE__存放文件編譯日期的字符串字面值
當調用一個函數傳入的實參可以和多個重載函數匹配,並且難以判斷孰優孰劣,則函數調用會產生二義性,編譯器會拒絕這個請求,例:
void func(int i, int j);
void func(double i, double j);
當調用時,func(1,1.2),對於第一個實參1,第一個函數更匹配,對於第二個實參1.2,第二個函數更匹配,則不能比較出調用誰更合適
(雖然可以相互轉換),則會產生二義性
既然編譯器會比較誰更匹配,那麼必然就有一個標準來決定匹配的優先級:
1.精確匹配:
實參和形參類型完全相同
從數組類型或函數類型(函數指針)轉換爲指針類型
頂層const的轉換
2.底層const間的轉換實現的匹配
3.整型提升實現的匹配
4.算術轉換或指針轉換實現的匹配
5.類類型轉換實現的匹配
整型提升的過程中,並不少就近原則,所有小整型都會提升爲int,如
void func(int);
void func(short);
func(‘a’); //將會提升爲int調用第一個,如果想調用第二個反而會導致類型轉換,這裏要提一下, char是一種特殊的整型
所有類型轉換的優先級都一樣,如:
void func(long);
void func(float);
func(3.14); //將會產生二義性,因爲都發生了類型轉換,值得一提的是3.14默認是double類型的,3.14f纔是float類型的
函數指針:
函數聲明:bool func(const string &, const string &);
該函數類型的指針bool (*p)(const string &, const string &); //函數類型由返回值和形參類型決定,該指針未初始化
bool *p(const string &, const string &); //括號必須帶上,否則爲返回值爲bool類型指針的函數的聲明
p= func;
p= &func;//兩個賦值方法是等價,&可省略
與上面的相同,當使用函數指針時不需要解引用指針
bool p1 = p(“aaa”, “bbb”);
bool p1 = (*p)(“aaa”, “bbb”);
bool p1 = func(“aaa”, “bbb”); //三者等價
函數指針不存在類型轉換規則
使用函數指針時,指針類型必須與函數類型精確匹配,意味着就算形參類型可以通過轉換得到也不行
void func(unsigned int);
void (*p)(int) = func; //錯誤,unsigned int和int不是精確匹配
與數組相同,形參類型雖然不能是函數類型(也可以寫成函數類型,不過會自動轉換成指向函數的指針類型),但可以是指向函數的指
針類型
void func(int);
void test(void (*p)(int)); //test的形參是一個函數指針
然而這樣寫會顯得很冗長,類型別名是一個不錯的選擇:
typedef void (*f)(int);指向形參爲1個整型,返回值爲void的函數的指針的類型別名爲f
typedef decltype(func) *f; //等價
使用類型別名後:
void test(f);
同理,雖然不能返回一個函數,但可以返回一個指針,然而與形參不同的是,函數類型並不會轉換成指針類型
using PF = void (*)(int);
using F = void (int);
PF f1(); //正確,PF是指向函數的指針,所有f1的返回值是指向函數的指針
F f1(); //錯誤,F是函數類型,f1不能返回一個函數
F *f1(); //正確,顯式的將返回類型指定爲指向函數的指針
沒有用到別名的寫法: void (*f1())(int);
同樣也可以使用decltype簡化
int f1(int);
decltype(f1) *returnF1(); //返回值是一個指向f1的指針