C++ primer閱讀筆記----------------函數

該博客用於記錄自己在閱讀過程中不懂的知識點,很少用到但比較重要的知識點以及模棱兩可的知識點

只存在與函數執行期間打對象叫自動對象,與之對應的是局部靜態對象


傳參時可以只用引用避免拷貝,拷貝大的對象效率低而且有些類類型不支持拷貝,使用引用還可以返回額外信息


實參初始化形參時會忽略頂層const,這就意味着

    void func(const int i);

    void func(int i);

並不是重載函數,會報出重複定義動錯誤,第一個func既可以傳入constint也可以傳入 intfunc並不能向i寫值。


形參的初始化和變量的初始化是一樣的


voidprint(const int[10]); //這裏的10只是我們的期望值,實際上不一定,因爲傳入的實際上是指針爲了限制數組的規格,我們一般採用3

種方法:

    1.使用標記//’\0’

    2.使用標準庫規範//beginend指針

    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_listvector類似,不一樣的是這個對象中的元素永遠是常量

    .size()表示對象中的元素數量

    .begin().end()首尾指針

   

    值得一提的是initializer_list對象之間賦值時不會拷貝列表中的元素/*list2(list)*/ /*list2=lsit*/拷貝完成後原始列表和副本共享元素

   

    傳參時不僅可以傳initializer_list對象也可以傳一對花括號包裹的值序列

   

    當形參類型不同時我們可以編寫一種特殊的函數,也就是所謂的可變參數模板(後面再提)



省略符形參是爲了方便C++程序訪問某些特殊的C代碼而設定的,這些代碼使用了名爲varargsC標準庫功能。通常省略符形參不應

該用與其他目地。


函數返回一個值的方式和初始化一個變量或形參的方式完全一樣

返回類型爲引用的函數得到左值,其他類型得到右值,可以像正常左值一樣使用返回函數的引用的調用

/*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 intint不是精確匹配


與數組相同,形參類型雖然不能是函數類型(也可以寫成函數類型,不過會自動轉換成指向函數的指針類型),但可以是指向函數的指

針類型

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的指針




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