1. 程式人生 > 實用技巧 >《c++入門經典》筆記14

《c++入門經典》筆記14

第十四章 高階函式

14.1過載成員函式

​ 函式可以進行過載,成員函式(成員方法)實質上也是一種函式,所以成員函式也可以進行過載。

程式清單14.1 Rectangle.cpp

#include <iostream>

class Rectangle
{
private:
    int width;
    int height;

public:
    Rectangle(int width, int height);
    ~Rectangle(){};

    void drawShape() const;
    void drawShape(int width, int height) const;
};

Rectangle::Rectangle(int width, int height)
{
    this->height = height;
    this->width = width;
}

void Rectangle::drawShape() const
{
    drawShape(width, height);
}

void Rectangle::drawShape(int width, int height) const
{
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            std::cout << "*";
        }
        std::cout << std::endl;
    }
}

int main()
{
    Rectangle box(30, 5);
    std::cout << "drawShape():" << std::endl;
    box.drawShape();
    std::cout << "\ndrawShape(40,2):" << std::endl;
    box.drawShape(40, 2);
    return 0;
}

​ 編譯器根據引數的型別和數值決定呼叫哪個版本。

14.2使用預設值

​ 常規函式可以有一個或多個預設值,類的成員函式也是如此。宣告預設值的規則也相同。

程式清單14.2 Rectangle2.cpp

#include <iostream>

class Rectangle
{
private:
    int width;
    int height;

public:
    Rectangle(int weight, int height);
    ~Rectangle() {}
    void drawShape(int aWidth, int aHeight, bool useCurrentValue = false) const;
};

Rectangle::Rectangle(int width, int height)
{
    this->width = width;
    this->height = height;
}

void Rectangle::drawShape(int aWidth, int aHeight, bool useCurrentValue) const
{
    int printWidth = 0;
    int printHeight = 0;

    if (useCurrentValue == true)
    {
        printWidth = width;
        printHeight = height;
    }
    else
    {
        printWidth = aWidth;
        printHeight = aHeight;
    }

    for (int i = 0; i < printHeight; i++)
    {
        for (int j = 0; j < printWidth; j++)
        {
            std::cout << "*";
        }
        std::cout << std::endl;
    }
}

int main()
{
    Rectangle box(20, 5);
    std::cout << "drawShape(0,0,true) ..." << std::endl;
    box.drawShape(0, 0, true);
    std::cout << "drawShape(25,4) ..." << std::endl;
    box.drawShape(25, 4);
    return 0;
}

14.3初始化物件

​ 與成員函式一樣,建構函式也可以過載。

​ 可以過載建構函式,但不能過載解構函式。解構函式的簽名總是這樣的:名稱為類名前加~,且不接受任何引數。

​ 建構函式由兩部分組成:初始化部分和函式體。可在初始化部分設定成員變數(即初始化列表),也可在建構函式的函式體內設定。初始化列表舉例:

Tricycle::Tricycle():speed(5),wheelSize(12)
{
    //函式體
}

​ 初始化成員變數的效率比在函式體內給他們賦值高。

14.4複製建構函式

​ 又稱拷貝建構函式

​ 除提供預設建構函式和解構函式之外,編譯器還提供一個預設複製建構函式。每當建立物件的備份時,都將呼叫複製建構函式。

​ 按值將物件傳入或傳出函式時,都將建立物件的一個臨時備份。如果物件是使用者定義的,就將呼叫相應類的複製建構函式。

​ 所有複製建構函式都接受一個引數:一個引用,它指向所屬類的物件。最好將該引用宣告為常量,因為複製建構函式不用修改傳入的物件,例如:Tricycle(const Tricycle &trike);

預設建構函式只作為引數傳入的物件的每個成員變數複製到新物件中,這稱為淺複製(淺拷貝)。雖然對大多數成員變數來說沒問題,但是不適用於成員變數是指向堆中物件的指標這種情況。

淺複製將一個物件的成員變數的值複製到另一個物件中,這導致兩個物件中的指標指向相同的記憶體地址。另一方面,深複製將堆記憶體中的值複製到新分配的堆記憶體中。

​ 淺複製使得兩個或多個變數指向相同記憶體,如果當中一個不再在作用域內,就會導致導致呼叫解構函式釋放分配的記憶體,而剩下的變數仍指向該記憶體,試圖訪問該記憶體將導致程式崩潰。

​ 對於這種問題,解決方案是自定義複製建構函式,並在複製時正確分配記憶體。

程式清單14.3 DeepCopy.cpp

#include <iostream>

class Tricycle
{
private:
    int *speed;

public:
    Tricycle();
    Tricycle(const Tricycle &);
    ~Tricycle();
    int getSpeed() const { return *speed; }
    void setSpeed(int newSpeed) { *speed = newSpeed; }
    void pedal();
    void brake();
};

Tricycle::Tricycle()
{
    speed = new int;
    *speed = 5;
}

Tricycle::Tricycle(const Tricycle &rhs)
{
    speed = new int;
    *speed = rhs.getSpeed();
}

Tricycle::~Tricycle()
{
    delete speed;
    speed = NULL;
}

void Tricycle::pedal()
{
    setSpeed(*speed + 1);
    std::cout << "\nPedaling " << getSpeed() << " mph" << std::endl;
}

void Tricycle::brake()
{
    setSpeed(*speed - 1);
    std::cout << "\nPedaling " << getSpeed() << " mph" << std::endl;
}

int main()
{
    std::cout << "Creating trike named wichita ...";
    Tricycle wichita;
    wichita.pedal();
    std::cout << "Creating trike named dallas ..." << std::endl;
    Tricycle dallas(wichita);
    std::cout << "wichita's speed: " << wichita.getSpeed() << std::endl;
    std::cout << "dallas's speed: " << dallas.getSpeed() << std::endl;
    std::cout << "setting wichita to 10 ..." << std::endl;
    wichita.setSpeed(10);
    std::cout << "wichita's speed: " << wichita.getSpeed() << std::endl;
    std::cout << "dallas's speed: " << dallas.getSpeed() << std::endl;
    return 0;
}

14.5編譯階段常量表達式

​ c++編譯器竭盡所能地提高程式的執行速度——儘可能對編寫的程式碼進行優化。一種存在效率提高空間的簡單情況是將兩個常量相加,如下所示:

const int decade = 10;
int year = 2016 + decade;

​ 2016與decade都是常量,所以編譯器將計算這個表示式並將結果2026儲存。因此,在編譯器看來,就像是將2026賦給了year一樣。

函式可使用const來返回常量值,如下所示:

const int getCentury()
{
    return 100;
}

你可能會認為如果呼叫語句int year = 2016 + getCentury();編譯器會對錶達式存在優化空間;雖然這個成員函式返回的是一個常量,但這個函式本身不是const的,它可能修改全域性變數或呼叫非const成員函式。

常量表達式是c++新增的功能,用關鍵字constexpr表示:

constexpr int getCentury()
{
    return 100;
}

常量表達式必須返回一個表示式,而該表示式只能包含字面值、其他常量表達式或使用constexpr定義的變數。

程式清單14.4 Circle.cpp

#include <iostream>

constexpr double getPi()
{
    return (double)22 / 7; //獲取Π的近似值
}

int main()
{
    float radius;
    std::cout << "Enter the radius of the circle: ";
    std::cin >> radius;
    double area = getPi() * (radius * radius);
    std::cout << "\nCircle's area: " << area << std::endl;
    return 0;
}