c++:-1

PamShao發表於2022-05-01

C++第一部分介紹基礎:c++:-0,本節介紹C++中函式使用。

函式

函式呼叫

呼叫函式需要先宣告函式原型
巢狀呼叫:

引數傳遞

  • 在函式被呼叫時才分配形參的儲存單元
  • 實參可以是常量、變數或表示式
  • 實參型別必須與形參相符
  • 值傳遞是傳遞引數值,即單向傳遞
  • 引用傳遞可以實現雙向傳遞
  • 常引用作引數可以保障實引數據的安全

傳引用比傳物件計算消耗小

可變引數

C++中提供了兩種方法:

  • 如果所有的實參型別相同,可以傳遞一個名為initializer_list的標準庫型別;
  • 如果實參的型別不同,我們可以編寫可變引數的模板。

initializer_list

initializer_list是一種標準庫型別,用於表示某種特定型別的值的陣列,該型別定義在同名的標頭檔案中

使用:

  • 使用模板時,我們需要在模板名字後面跟一對尖括號,括號內給出型別引數。例如:
    initializer_list ls; // initializer_list的元素型別是string
    initializer_list li; // initializer_list的元素型別是int
  • initializer_list比較特殊的一點是,其物件中的元素永遠是常量值,我們無法改變initializer_list物件中元素的值。
  • 含有initializer_list形參的函式也可以同時擁有其他形參

應用:

  • 在編寫程式碼輸出程式產生的錯誤資訊時,最好統一用一個函式實現該功能,使得對所有錯誤的處理能夠整齊劃一。然而錯誤資訊的種類不同,呼叫錯誤資訊輸出函式時傳遞的引數也會各不相同。
  • 使用initializer_list編寫一個錯誤資訊輸出函式,使其可以作用於可變數量的形參。

行內函數

作用:編譯時在呼叫處用函式體進行替換,節省了引數傳遞、控制轉移等開銷。

宣告時使用關鍵字: inline
注意:

  • 行內函數體內不能有迴圈語句和switch語句;
  • 行內函數的定義必須出現在行內函數第一次被呼叫之前;
  • 對行內函數不能進行異常介面宣告。

常量表示式

C++-11中新增,用於初始化常量表示式
(1)constexpr函式語法規定

  • constexpr修飾的函式在其所有引數都是constexpr時,一定返回constexpr;
  • 函式體中必須有且僅有一條return語句。

(2)constexpr函式舉例

constexpr int get_size() { return 20; }
constexpr int foo = get_size();  //正確:foo是一個常量表示式

函式預設引數值

(1)預設引數值的說明次序

  • 有預設引數的形參必須列在形參列表的最右,即預設引數值的右面不能有無預設值的引數;
  • 呼叫時實參與形參的結合次序是從左向右。
    例:
    int add(int x, int y = 5, int z = 6);//正確
    int add(int x = 1, int y = 5, int z);//錯誤
    int add(int x = 1, int y, int z = 6);//錯誤
    (2)預設引數值與函式的呼叫位置
  • 如果一個函式有原型宣告,且原型宣告在定義之前,則預設引數值應在函式原型宣告中給出;如果只有函式的定義,或函式定義在前,則預設引數值可以函式定義中給出。
    例:

過載函式

函式名相同,引數型別和返回型別不同;引數個數不同



系統函式

C++的系統庫中提供了幾百個函式可供程式設計師使用,例如:

  • 求平方根函式(sqrt)
  • 求絕對值函式(abs)
  • 正弦值、餘弦值和正切值的函式:sin()、cos()、tan()
    使用系統函式時要包含相應的標頭檔案,例如:cmath

舉例

計算n次方

計算x的n次方

#include <iostream>

using namespace std;
//計算x的n次方
double power(double x, int n) {
    double val = 1.0;
    while (n--) val *= x;
    return val;
}
int main() {
    cout << "5 to the power 2 is "
         << power(5, 2) << endl;
    return 0;
}

進位制轉換(8-10)

輸入一個8位二進位制數,將其轉換為十進位制數輸出。

#include <iostream>
using namespace std;

double power (double x, int n); //計算x的n次方

int main() {
    int  value = 0;
    cout << "Enter an 8 bit binary number  ";
    //cin:只有在輸入完資料再按Enter鍵後,該行資料才被送入鍵盤緩衝區,形成輸入流,提取運算子“>>”才能從中提取資料。需要注意保證從流中讀取資料能正常進行。
    for (int i = 7; i >= 0; i--) {
        char ch;
        cin >> ch;
        if (ch == '1')
            value += static_cast<int>(power(2, i));//tatic_cast()強制型別轉換
    }
    cout << "Decimal value is  " << value << endl;
    return 0;
}
double power (double x, int n) {
    double val = 1.0;
    while (n--)
        val *= x;
    return val;
}

計算π

π的計算公式如下:

#include <iostream>
using namespace std;
//求arctan
double arctan(double x) {
    double sqr = x * x;
    double e = x;
    double r = 0;
    int i = 1;
    while (e / i > 1e-15) {
        double f = e / i;
        r = (i % 4 == 1) ? r + f : r - f;
        e = e * sqr;
        i += 2;
    }
    return r;
}

int main() {
    double a = 16.0 * arctan(1/5.0);
    double b = 4.0 * arctan(1/239.0);
    //注意:因為整數相除結果取整,如果引數寫1/5,1/239,結果就都是0
    cout << "PI = " << a - b << endl;
    return 0;
}

尋找回文數

尋找並輸出11~999之間的數M,它滿足\(M、M^2和M^3\)均為迴文數。
迴文:各位數字左右對稱的整數。
例如:11滿足上述條件:112=121,113=1331。

分析:
用除以10取餘的方法,從最低位開始,依次取出該數的各位數字。按反序重新構成新的數,比較與原數是否相等,若相等,則原數為迴文。

#include <iostream>

using namespace std;
//判斷n是否為迴文數
bool symm(unsigned n) {
    unsigned i = n;
    unsigned m = 0;
    while (i > 0) {
        m = m * 10 + i % 10;
        i /= 10;
    }
    return m == n;
}

int main() {
    for(unsigned m = 11; m < 1000; m++)
        if (symm(m) && symm(m * m) && symm(m * m * m)) {
            cout << "m = " << m;
            cout << "  m * m = " << m * m;
            cout << "  m * m * m = "
                 << m * m * m << endl;
        }
    return 0;
}

分段函式

計算分段函式,並輸出結果


分析:
計算\(sin(x)\)的公式,精度為\(10^{-10}\)

#include "iostream"
#include "cmath"

using namespace std;

const double T=1e-10; //定義計算精度10^{-10}

//計算sin(x)
double tsin(double x)
{
    double g=0;
    double t=x;
    int n=1;
    do{
        g+=t;
        n++;
        t=-t*x*x/(2*n-1)/(2*n-2);
    }while(fabs(t)>=T);//fabs:絕對值
    return g;
}

int main()
{
    double k,r,s;
    cout << "r=";
    cin >>r;
    cout << "s=";
    cin >>s;
    if(r*r<=s*s)
    {
        k= sqrt(tsin(r)*tsin(r)+tsin(s)*tsin(s));
    }else
        k=tsin(r*s)/2;
    cout <<k <<endl;
    return 0;
}

擲骰子

每個骰子有六面,點數分別為1、2、3、4、5、6。遊戲者在程式開始時輸入一個無符號整數,作為產生隨機數的種子。每輪投兩次骰子,第一輪如果和數為7或11則為勝,遊戲結束;和數為2、3或12則為負,遊戲結束;和數為其它值則將此值作為自己的點數,繼續第二輪、第三輪...直到某輪的和數等於點數則取勝,若在此前出現和數為7則為負。

分析:
(1)rand函式
函式原型:int rand(void);
所需標頭檔案:
功能和返回值:求出並返回一個偽隨機數

(2)srand函式
void srand(unsigned int seed);
引數:seed產生隨機數的種子
所需標頭檔案:
功能:為使rand()產生一序列偽隨機整數而設定起始點。使用1作為seed引數,可以重新初化rand()。

#include "iostream"
#include "cmath"

using namespace std;
enum GameStatus{Win,Lose,Playing};//列舉儲存狀態

//擲骰子,計算和數,輸出和數
int rollDice()
{
    int die1=1+rand()%6;
    int die2=1+rand()%6;
    int sum=die1+die2;
    cout << die1<<"+"<<die2<<"="<<sum<<endl;
    return sum;
}


int main()
{
    int sum,myPoint;
    GameStatus status;
    unsigned seed;
    int rollDice();
    cout <<"請輸入種子:";
    cin >> seed;
    srand(seed);//將種子傳給rand()
    sum=rollDice();//第一輪擲骰子
    switch (sum) {
        case 7:
        case 11:
            status=Win;
            break;
        case 2:
        case 3:
        case 12:
            status=Lose;
            break;
        default:
            status=Playing;
            myPoint=sum;
            cout << "點數為"<<myPoint<<endl;
            break;
    }
    //第二輪以後
    while (status==Playing)
    {
        sum=rollDice();
        if(sum==myPoint) //某輪和數等於點數取勝
        {
            status=Win;
        }else if(sum ==7)//出現和數為7則負
        {
            status=Lose;
        }
    }
    //輸出
    if(status==Win)
    {
        cout << "Win"<<endl;
    } else
        cout << "Lose" <<endl;
    return 0;
}

疑問:為什麼die1和die2是一樣的?
種子一樣!

    srand(2);
    int die1=1+rand()%6;
    srand(1);
    int die2=1+rand()%6;
    cout<<die1<<","<<die2<<endl;

求組合數

#include <iostream>

using namespace std;
int commit(int n,int k)
{
    if(k>n)
        return 0;
    else if(n==k || k==0)
        return 1;
    else
        return commit(n-1,k)+ commit(n-1,k-1);
}

int main() {
    int n,k;
    cout << "請輸入n和k:";
    cin >> n>>k;
    cout <<"C("<<n<<","<<k<<")="<< commit(n,k)<<endl;
    return 0;
}

漢諾塔問題

有三根針A、B、C。A針上有N個盤子,大的在下,小的在上,要求把這N個盤子從A針移到C針,在移動過程中可以藉助B針,每次只允許移動一個盤,且在移動過程中在三根針上都保持大盤在下,小盤在上。

分析:
將n 個盤子從A針移到C針可以分解為三個步驟:

  • 將A 上n-1個盤子移到 B針上(藉助C針);
  • 把A針上剩下的一個盤子移到C針上;
  • 將n-1個盤子從B針移到C針上(藉助A針)。
#include <iostream>
using namespace std;

//將src針的最上面一個盤子移動到dest針上
void move(char src, char dest) {
    cout << src << " --> " << dest << endl;
}

//將n個盤子從src針移動到dest針,以medium針作為中轉
void hanoi(int n, char src, char medium, char dest)
{
    if (n == 1)
        move(src, dest);
    else {
        //將A 上n-1個盤子移到 B針上(藉助C針);
        hanoi(n - 1, src, dest, medium);
        //把A針上剩下的一個盤子移到C針上;
        move(src, dest);
        //將n-1個盤子從B針移到C針上(藉助A針)
        hanoi(n - 1, medium, src, dest);
    }
}
int main() {
    int m;
    cout << "Enter the number of diskes: ";
    cin >> m;
    cout << "the steps to moving " << m << " diskes:" << endl;
    hanoi(m,'A','B','C');
    return 0;
}

值交換

輸入兩個整數並交換
分析:
引數傳遞有兩種:值傳遞和引用傳遞
引用就是別名,定義int &a=i;

值傳遞,並沒有交換

#include<iostream>
using namespace std;
void swap(int a, int b) {
    int t = a;
    a = b;
    b = t;
}

int main() {
    int x = 5, y = 10;
    cout<<"x = "<<x<<"  y = "<<y<<endl;
    swap(x, y);
    cout<<"x = "<<x<<"  y = "<<y<<endl;
    return 0;
}

執行結果:
x = 5      y = 10
x = 5      y = 10

引用傳遞:

#include<iostream>
using namespace std;

//a和b分別是x和y的引用
void swap(int& a, int& b) {
    int t = a;
    a = b;
    b = t;
}

int main() {
    int x = 5, y = 10;
    cout<<"x = "<<x<<"  y = "<<y<<endl;
    swap(x, y);
    cout<<"x = "<<x<<"  y = "<<y<< endl;
    return 0;
}
x = 5  y = 10
x = 10  y = 5

行內函數

#include <iostream>
using namespace std;

const double PI = 3.14159265358979;
inline double calArea(double radius) {
    return PI * radius * radius;
}

int main() {
    double r = 3.0;
    double area = calArea(r);
    cout << area << endl;
    return 0;
}

計算長方體的體積

有三個形參:length(長)、width(寬)、height(高),其中width和height帶有預設值2和3。

#include <iostream>
#include <iomanip>
using namespace std;

int getVolume(int length, int width = 2, int height = 3);//宣告在前,定義預設引數值
int main() {
    const int X = 10, Y = 12, Z = 15;
    cout << "Some box data is " ;
    cout << getVolume(X, Y, Z) << endl;
    cout << "Some box data is " ;
    cout << getVolume(X, Y) << endl;
    cout << "Some box data is " ;
    cout << getVolume(X) << endl;
    return 0;
}
//定義中不定義預設引數值
int getVolume(int length, int width, int height) {
    cout << setw(5) << length << setw(5) << width << setw(5)
         << height << '\t';//setw用於設定欄位的寬度
    return length * width * height;
}

過載函式

編寫兩個名為sumOfSquare的過載函式,分別求兩整數的平方和及兩實數的平方和。

#include <iostream>
using namespace std;
//型別不同
int sumOfSquare(int a, int b) {
    return a * a + b * b;
}
double sumOfSquare(double a, double b) {
    return a * a + b * b;
}
int main() {
    int m, n;
    cout << "Enter two integer: ";
    cin >> m >> n;
    cout<<"Their sum of square: "<<sumOfSquare(m, n)<<endl;
    double x, y;
    cout << "Enter two real number: ";
    cin >> x >> y;
    cout<<"Their sum of square: "<<sumOfSquare(x, y)<<endl;
    return 0;
}

求正弦值、餘弦值和正切值

從鍵盤輸入一個角度值,求出該角度的正弦值、餘弦值和正切值。

#include <iostream>
#include <cmath>
using namespace std;
const double PI = 3.14159265358979;

int main() {
    double angle;
    cout << "Please enter an angle: ";
    cin >> angle;  //輸入角度值
    double radian = angle * PI / 180; //轉為弧度
    cout << "sin(" << angle << ") = " << sin(radian) <<endl;
    cout << "cos(" << angle << ") = " << cos(radian) <<endl;
    cout << "tan(" << angle << ") = " << tan(radian) <<endl;
    return 0;
}

習題

(1)已知函式FA呼叫FB,若要把這兩個函式定義在同一個檔案中,則

  • FA必須定義在FB之前
  • FB必須定義在FA之前
  • 若FA定義在FB之後,則FA的原型必須出現在FB的定義之前
  • 若FB定義在FA之後,則FB的原型必須出現在FA的定義之前(對)

函式原型,就是函式的宣告

(2)在()時為形參分配儲存空間。

  • 函式宣告
  • 函式定義
  • 函式呼叫(對)

(3)可以定義指向引用的指標.
錯。因為引用不是物件,引用並沒有在程式中佔據記憶體空間,故沒有地址的說法.
(4)類內實現好的成員函式是行內函數,在類體外實現的函式不能是行內函數
錯。因為行內函數主要的作用是在某些情況(某個函式被呼叫多次)下可以提高程式的執行效率。定義行內函數,可以顯式用inline宣告,也可以直接在類內定義好實現. 擴充套件閱讀
(5)已知程式中有以下宣告:

  • int nonconst_var = 100;
  • const int const_var1 = 2;
  • const int const_var2 = nonconst_var;
    則下述程式碼中正確的是:
  • constexpr int constexpr_var1 = 3 + const_var1 * 4; (對)
  • constexpr int constexpr_var2 = 3 + nonconst_var * 4;
  • constexpr int constexpr_var3 = 3 + const_var2 * 4;

分析:
constexpr的變數的值必須是編譯器在編譯的時候就可以確定的。上例中因為nonconst_var的值在語法上來講,執行期間可能被更改,所以編譯期間無法確定,不屬於常數表示式。因為const_var2是由非常數表示式來初始化的,所以const_var2也不是常數表示式。但const_var2本身的宣告,定義及初始化是合法的。constexpr比const更嚴格,用來初始化constexpr_var2和constexpr_var3的也都不是常數表示式,所以他們的定義都是錯誤的。
(6)例3-15中的getVolume函式,如果直接呼叫int a=getVolume();後,會有什麼樣的結果?

  • 編譯執行正確,a的值為0
  • 編譯執行正確,a的值為6
  • 編譯報錯(對)
  • 執行出錯
    分析:
    函式有一個形參沒有預設值,所以至少要提供一個實參。注意,預設不是0

(7)判斷兩個浮點數是否相等

abs(a-b)<1e-10  //abs求絕對值

相關文章