1. 程式人生 > 其它 >C++ 函式(一)引數傳遞/return語句/返回型別

C++ 函式(一)引數傳遞/return語句/返回型別

C++ 函式(一)引數傳遞/return語句/返回型別

6.1 基礎

函式的返回值不能是陣列或函式型別,但可以是指向陣列或函式的指標

6.1.1 作用域,區域性物件

函式體是一個塊,構成一個新的作用域,其中定義的變數和形參都是區域性變數

在函式體外部定義的物件存在於程式的整個執行過程中

自動物件:

只存在於塊執行期間的物件,例如形參,函式終止,形參會被銷燬,區域性變數如果本身不帶初始值,則會執行預設初始化,意味內建型別的未初始化區域性變數將產生未定義的值

區域性靜態物件

定義為static型別,如果沒有顯式的初始值,則將初始化為0

函式宣告

又叫函式原型,不包含函式體,無需形參名(也可以有)

6.2 引數的傳遞

pass by value

將實參的值拷貝後賦值給形參,對形參的操作不會影響實參

指標形參

傳遞指標,拷貝的是指標的值,拷貝後的兩個指標是不同的指標,但是由於指標可以間接的訪問所指的物件,所以可以通過指標修改它所指的物件值

void reset(int *p){ 
	*p = 0;//改變了指向的物件的值
	 p = 0;//只改變了p的區域性拷貝,實參未改變 
}

C++建議用引用型別的形參代替指標

pass by reference

void reset(int &p){}

改變引用形參,就是改變初始化形參的實參

儘量使用引用來避免拷貝,而且有些類不支援拷貝(比如IO類)

6.2.2 const形參,const實參

頂層const作用於物件本身,形參有頂層const時,傳遞常量或非常量物件都可以(實參初始化形參時會忽略頂層const)

void func(const int n){}
const int i = 1;
int j = 2;
func(i);
func(j);
//上面兩種呼叫方法都正確
//由於這種忽略,導致下面兩種函式其實是一樣的,所以不是過載
void func(const int i);
void func(int i);

6.2.3 指標,引用形參

可以使用非常量初始化一個底層const,但是不能反過來,一個普通的引用必須用同類型的物件初始化

int i = 0;
const int ci = i;
string::size_type ctr = 0;
reset(&i);//形參型別為int*
reset(ci);//錯誤,不能使用const int物件的指標去初始化int*
reset(i);//形參為int&時
reset(ci);//不能將普通引用繫結到const物件ci上
reset(42);//不能繫結字面值
reset(ctr);//型別不匹配,ctr是無符號型別

可以使用字面值初始化常量引用

儘量使用常量引用,避免限制函式能接收的實參型別(字面值)

6.2.4 陣列形參

陣列的兩個特殊性質:

  1. 不能拷貝陣列:無法值傳遞
  2. 使用陣列時通常轉化成指標:傳遞引數時,實際上傳遞的是指向陣列首元素的指標

陣列的大小對函式呼叫沒影響

管理指標形參:(主要是長度問題)

一種方法是使用標記指定長度,只適用於C語言中類似C風格字串,以空字元結束

所以更實用的是另外兩種方法:

  1. 標準庫函式begin,end

    void print(const int *beg,const int *end){
    	while(beg!=end){
    		cout<<*beg++<<endl;
    	}
    }
    int j[2] = {0,1};
    print(begin(j),end(j));
    
  2. 顯式傳遞一個數組大小形參

    print(j,end(j)-begin(j));
    

陣列引用形參/const形參

不需要改變陣列元素值時,最好加上const

//陣列的引用,維度是型別的一部分
void print(int (&arr)[10]){}
//必須加(),否則arr就是引用的陣列,而不是具有10個整數的整型陣列的引用
//傳遞引數時,只能將函式作用域大小為10的陣列
int i[2] = {2,1};
print(i);//錯誤

多維陣列

C++的多維陣列就是陣列的陣列

陣列的陣列,首元素本身就是一個數組,第二維不能省略

void print(int (*matrix)[10],int rowsize);
//如果沒有(),則是十個指標構成的陣列,而不是指向含有10個整數的陣列的指標
void print(int matrix[][10],int rowsize);

6.2.5 main(int argc,char *argv[])

6.2.6 可變形參

處理不同數量的實參的函式:

  1. 如果所有實參的型別相同,可以傳遞標準庫型別:initializer_list

  2. 不同則寫可變引數模板

  3. 省略符(一般只用於與C函式互動的介面程式)

    形式:void fun(parm_list,...)

    void fun(...)

    僅用於C和C++通用的型別

initializer_list物件中的元素永遠是常量值

拷貝,賦值一個initializer_list物件不會拷貝列表中的元素,拷貝後原始列表和副本共享元素

list2(list1);//拷貝
list2 = list1;//賦值


void error_msg(initializer_list<string> il){
    //initializer_list有begin,end,所以可以使用指標,範圍for迴圈遍歷列表
}
//呼叫:expected,actuall為string物件
error_msg({"functionX",expected,actuall});

6.3 返回值 (return)

無返回值的return後可以什麼都沒有,也可以跟另外一個返回void的函式

不要返回區域性物件的引用或指標

函式結束後,區域性變數的記憶體空間會被釋放

const string&manip(){//下面兩個都錯
	string ret;
	if(){
		return ret;//區域性物件
	}
	else{
		return "empty";//臨時區域性變數
	}
}

引用返回左值:

呼叫運算子優先順序和點,箭頭相同

//符合左結合律,返回指標,引用,類的物件:
auto s = func(s1,s2).size();

呼叫返回引用的函式會得到左值,其他型別為右值

因此這種情況下函式的返回值可以像使用其他左值一樣:

char &get_val(string &str,string::size_type ix){
	return str[ix];
}
int main()
{
	string s("a value");
	get_val(s,0) = 'A';
	//s[0]被改為A
    return 0;
}
//返回值是個常量引用就不能賦值了

列表初始化返回值

vector<string>pro(){
	return {"one","two"};
}

如果返回內建型別則{}中最多包含一個值

返回陣列指標

陣列不能拷貝,所以函式不能返回陣列

使用類型別名簡化返回陣列的指標和引用

//下面兩行程式碼是等價的
typedef int arr[10];
using arr = int[10];
//返回一個指向含有十個整數的陣列的指標
arr* func(int i);
//如果不使用類型別名:
Type (*function(parameter_list))[dimension]
int (*func(parameter_list))[10];
//必須有*之外的(),否則宣告的函式返回型別就會是指標的陣列,而不是陣列的指標

尾置返回:

函式真正的返回型別在形參列表之後,格式:

auto func(int i)->int(*)[10];
//任何函式定義都能使用尾置返回

decltype

如果知道函式返回的指標指向哪個陣列,則可以使用decltype

int o[] = {12,3};
int o2[] = {1,2};
decltype(o)* arrptr(int i){}

decltype的結果是陣列,所以還要加上*