文章程式碼顯示

2018年3月11日 星期日

《筆記》談談C++語言 - 2:函式輸入引數預設值、函式的多載、函式樣版

在實際寫類別(class)之前,我們先概略的講述 C++ 與 C 語言基本保留字使用上的差異

函式預設引數

#include "stdio.h"
#include "stdlib.h"

void printValue( int a = 55, int b = 66, int c = 77);

int main() {

 printValue();
 printValue( 1 );
 printValue( 1, 2 );
 printValue( 1, 2, 3 );

 return 0;
}

void printValue( int a, int b, int c){
 printf( "a = %d, b = %d, c = %d\n", a, b, c );
}




程式常常會以重複同樣的引數呼叫同一個函式。在這種情況下我們可以指定此函式具有預設引數(default argument),也就是輸入參數的預設値

當呼叫具有預設引數的函數時,編譯器就會自動插入該引數的預設值。

且預設引數的個數以及順序有一定的關係,我們直接由結果來進行理解

當我們完全沒有指定輸入引數的時候,則 a b c 三個參數都會使用預設引數

雖然我們已經對該函數的輸入參數做預設值的設定了,但我們仍然可以在呼叫函式的時候使用自己想要的值,此時編譯器就會改用這個你輸入的參數值,而不使用預設值。

若只輸入一個値,則會自動把該輸入引數指定給變數 a

若輸入兩個値,則會自動把該輸入引數指定給變數 a 以及 b

若輸入三個値,則會自動把該輸入引數指定給所有變數

註:預設引數應該要放置在「函式原型」(第 4 行)的地方
且「函式標頭」(第 16 行) 的地方不能進行指定

函式的多載

C++ 允許定義多個函式擁有相同的名稱,但

其參數串列必須不同(最起碼參數型別或參數個數或順序不能一樣)

這種功能就稱為函式的多載(function overloading)

當呼叫一個多載函式時,編譯器會檢查此函式呼叫中的引數個數、型別、順序,以便選擇適當的函式

函式的多載通常用來建立幾個同名函式,而這些函式會對不同的資料型別執行 "類似" 的工作,甚至我們可以設計成針對不同的資料型別就使用不同的程式邏輯(演算法)來處理不同的資料型別,例如數學函式庫(math.h)中可處理不同的數值資料型態。(亦常用在類別(class)的建構子,與預設引數配合,藉由引數的個數來進行不同的初使值給定,後述)


#include "stdio.h"
#include "stdlib.h"

int square( int x){
 return x*x;
}

double square( double x){
 return x*x;
}

int main() {
 printf("5 * 5 = %d\n", square(5) );
 printf("1.3 * 1.3 = %f", square(1.3) );
 return 0;
}

我們在第 4 行以及第 8 行的地方都宣告了 square 函式,但其輸入參數以及回傳參數的型態是不同的(分別為 int 或 double),結果顯示,編譯器會自動去找到適合的函式來進行調用

更為典型的應用在於設計成針對不同的資料型別就使用不同的程式邏輯(演算法)來處理不同的資料型別,範例如下

#include "stdio.h"
#include "stdlib.h"

int func( int x){
 printf("The type of input parameter is 'int'\n");
 return x*x;
}

double func( double x){
 printf("The type of input parameter is 'double'\n");
 return 100+(x*x);
}

int main() {
 printf("5 * 5 = %d\n", func(5) );
 printf("1.3 * 1.3 = %f", func(1.3) );
 return 0;
}



將函式預設引數與函式的多載進行結合,則為以下

#include "stdio.h"
#include "stdlib.h"

void func( int x = 5){
 printf("case(1), x = %d\n",x);
}

void func( int x, int y){
 printf("case(2), x = %d, y = %d\n", x, y);
}

void func( int x, int y, int z){
 printf("case(3), x = %d, y = %d, z = %d\n", x, y, z);
}

int main() {

 func();
 func(1);
 func(1,2);
 func(1,2,3);

 return 0;
}

我們在第 4 、8 及 12 行都宣告了 func 函數,但三個函數的輸入參數都是不同的

在第 18 - 21 行的地方我們連續調用了四次 func 函數,可以看出編譯器自動藉由輸入參數的多寡來調用合適的函式

補充 :

1. 編譯器如何分辨多載函式?

多載函式是以函式的 "簽名式(signature)" 來區分,該簽名式在組何語言中會將函式標頭(如上方的 4, 8, 12 行) 寫成一個縮寫的方式,且每個編譯器的方式不同。如 GNU C++編譯器,縮寫會包含「底線符號、字母z、一個數字(代表函式名稱由多少個字元組成)、各個輸入參數的型別(如 int 用 i 進行縮寫;char 用 c ;int& 用 Ri ....  )」

因為這樣的特性,所以你不能定義兩個輸入參數列相同,但「回傳值的型別」不同的多載函式。(因為兩個簽名式會是相同的)

函式樣版

函式的多載後半部我們示範了將 int 及 double 型別的變數傳遞進多載函式 func 中,並將 func 函數設計成針對不同的資料型別就使用不同的程式邏輯(演算法)

函式樣版的使用則是針對於 "對不同的資料型別,仍執行一模一樣的程式邏輯(演算法)" 的場合,範例程式碼如下

// main.cpp

#include "stdio.h"
#include "stdlib.h"
#include "maximum.h"

int main() {

 int NUM1 = 1;
 int NUM2 = 2;
 int NUM3 = 3;

 printf("Maximum Number = %d \n", maximum(NUM1,NUM2,NUM3) );

 double NUM4 = 1.2;
 double NUM5 = 2.4;
 double NUM6 = 3.6;

 printf("Maximum Number = %f \n", maximum(NUM4,NUM5,NUM6) );

 return 0;
}

// maximum.h

template < class T >
T maximum (T num1, T num2, T num3){

 T MaxNum = num1;

 if ( num2 > MaxNum )
  MaxNum = num2;
 if ( num3 > MaxNum )
  MaxNum = num3;

 return MaxNum;
}


首先我們必須在 maximum.h 建立一個函式樣版。

函式樣版定義的第一個必須是關鍵字 template ,接著鍵入左右角括號將保留字 class 以及正規化型別參數名稱(在此我隨意命名為 T)包裹起來,如  < class T  >

此時這個正規化型別參數名稱( T ) 就如同一個型別

接著我們就開始定義函式樣本,其定義的方法與正常函式一樣,為一不同處是用 正規化型別參數名稱( T ) 來替代真正的型別 ( 4 ~ 14 行)

我們在 main.cpp 中使用 #include "maximum.h" 將這個函式樣版呼叫進來

於程式碼 9 ~ 13 行中我們利用 maximum 函式找出 NUM1 ~ NUM3 的最大值,並回傳最大值。此時編譯器會視為 maximum 函式如以下的定義


int maximum (int num1, int num2, int num3){

 int MaxNum = num1;

 if ( num2 > MaxNum )
  MaxNum = num2;
 if ( num3 > MaxNum )
  MaxNum = num3;

 return MaxNum;
}

也就是將樣版中所有的 T 以 int 取代

同樣的,於程式碼 15 ~ 19 行中我們利用 maximum 函式找出 NUM4 ~ NUM6 的最大值,並回傳最大值。此時編譯器會視為 maximum 函式如以下的定義


double maximum (double num1, double num2, double num3){

 double MaxNum = num1;

 if ( num2 > MaxNum )
  MaxNum = num2;
 if ( num3 > MaxNum )
  MaxNum = num3;

 return MaxNum;
}

也就是將樣版中所有的 T 以 double 取代

↓↓↓ 連結到部落格方針與索引 ↓↓↓

Blog 使用方針與索引