C++類別別物件作為函數引數傳遞詳解

2020-07-16 10:04:40
我們知道了如何使用變數作為函數的實參,類物件也可以作為實參傳遞給函數

例如,以下函數具有一個接收 Rectangle 物件的形參:

void displayRectangle(Rectangle r)
{
    cout << "Length = " << r.getLength() << endl;
    cout << "Width = " << r.getWidth() << endl;
    cout << "Area = " << r.getArea() << endl;    '
}

以下程式碼建立了一個長度為 15 和寬度為 10 的 Rectangle 物件 box,然後將 box 物件作為引數傳遞給了 displayRectangle 函數:

Rectangle box(15, 10);
displayRectangle(box);

假設 Rectangle 類包含本範例中使用的成員函數 displayRectangle,則該函數將輸出以下資訊:

Length = 15
Width = 10
Area = 150

與常規變數一樣,物件可以通過值或參照傳遞給函數。在 Rectangle 範例中,box 通過值傳遞給 displayRectangle 函數,這意味著 displayRectangle 函數會收到一個 box 的副本。如果 displayRectangle 呼叫任何 Rectangle 類設定器函數,那麼它們將只會更改 box 的副本,而不是原來的 box。如果函數需要儲存或更改物件成員變數中的資料,則必須通過參照將物件傳遞給它。

下面的程式說明了這一點:
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

class InventoryItem
{
    private:
        int partNum;    // Part number
        string description;    // Item description
        int onHand;    // Units on hand
        double price;    // Unit price
    public:
        void storeInfo(int p, string d, int oH, double cost); // Prototype
        int getPartNum()
        {
            return partNum;
        }
        string getDescription()
        {
            return description;
        }
        int getOnHand()
        {
            return onHand;
        }
        double getPrice()
        {
            return price;
        }
};

void InventoryItem::storeInfo(int p, string d, int oH, double cost) {
    partNum = p;
    description = d;
    onHand = oH;
    price = cost;
}

//Function prototypes for client program
void storeValues (InventoryItem&);// Receives an object by reference
void showValues (InventoryItem); // Receives an object by value

int main()
{
    InventoryItem part;    // part is an Inventoryltem object
    storeValues(part);
    showValues(part);
    return 0;
}
void storeValues(InventoryItem &item)
{
    int partNum;    // Local variables to hold user input
    string description;
    int qty;
    double price;
    // Get the data from the user
    cout << "Enter data for the new part number n";
    cout << "Part number: ";
    cin >> partNum;
    cout << "Description: ";
    cin.get();
    getline (cin, description);
    cout << "Quantity on hand: ";
    cin >> qty;
    cout << "Unit price: ";
    cin >> price;
    item.storeInfo(partNum, description, qty, price);
}
void showValues(InventoryItem item)
{
    cout << fixed << showpoint << setprecision(2) << endl;
    cout << "Part Number : " << item.getPartNum() << endl;
    cout << "Description : " << item.getDescription () << endl;
    cout << "Units On Hand: " << item.getOnHand () << endl;
    cout << "Price : $" << item.getPrice() << endl;
}
程式輸出結果:

Enter data for the new part number
Part number: 175
Description: Hammer
Quantity on hand: 12
Unit price: 7.49

Part Number : 175
Description : Hammer
Units On Hand: 12
Price : $7.49

程式中有兩個函數接收 InventoryItem 物件。該物件通過參照傳遞給 storeValues,因為該函數需要呼叫一個類設定器函數,將新值儲存到物件中。該物件通過值傳遞給 showValues,因為此函數只需要使用存取器函數來檢索和使用儲存在物件資料成員中的值。

程式中還可以看到,Inventoryltem 類宣告出現在 storeValues 和 showValues 函數的原型之前,這一點很重要,因為這兩個函數都有一個 Inventoryltem 物件作為形參,所以編譯器在遇到任何參照它的語句之前,必須知道 Inventoryltem 是什麼,否則就會發生錯誤。

常數參照形參

在上面的程式中,InventoryItem 物件按值傳遞給 showValues 函數。但是,按值傳遞物件需要複製所有物件成員的副本,這可能會減慢程式的執行時間,如果物件有很多成員,則更是如此。另一方面,當按參照傳遞物件時,由於該函數可以存取原始物件,而不必進行任何複製,所以它比通過值傳遞更快,正因為如此,一般更願意按參照傳遞物件。

但是,按參照傳遞物件有一個缺點,因為該函數可以存取原始物件,所以它可以呼叫其設定器函數更改物件成員資料。這就是為什麼當程式設計師想要保護物件的內容時,通常不會按參照傳遞變數。

幸運的是這個問題有解決辦法。為了保護物件讓它作為實參傳遞,而又不必複製副本,可以將它作為常數參照進行傳遞,這意味著原始物件作為參照被傳遞給了函數,但是它不能呼叫任何設定器函數或更改物件的成員資料,它只能呼叫自己被指定為常數函數的存取器函數。

要將形參宣告為常數參照形參,必須將關鍵字 const 放在函數原型和函數頭的形參列表中。對照上面程式中的 showValues 函數的函數原型和函數頭,如果將它改為使用常數參照形參,則語句如下:

void showValues (const InventoryItem&) //函數原型
void showValues (const InventoryItem &item) // 函數頭

現在,showValues 函數只能呼叫 InventoryItem 函數,也可以在函數原型和函數頭中列出關鍵字 const,如下所示:

double getPrice() const

如果 showValues 嘗試呼叫任何其他 InventoryItem 函數,則會發生編譯器錯誤。請注意,當 showValues 被修改為具有常數參照形參時,只有函數原型和函數頭被更改為包含關鍵字 const。showValues 函數的主體和對 showValues 的呼叫不會改變。

從函數返回一個物件

正如函數可以編寫為返回一個 int、double 或其他資料型別一樣,它們也可以設計為返回一個物件。

事實上,當以前從函數返回一個字串時,就已經是在這樣做了,因為字串就是一個物件。當函數返回一個物件時,它通常會建立該類的區域性範例,設定其資料成員,然後返回它。

以下仍以上面程式為例,說明如何在 storeValues 函數中建立 InventoryItem 物件,然後返回到呼叫函數。請注意,這個新版本的 storeValues 函數不接收任何實參,它的返回型別現在是 InventoryItem 而不是 void。
InventoryItem storeValues()
{
    InventoryItem templtem; // Inventoryltem 區域性物件
    int partNum;    //儲存使用者輸入的區域性變數
    string description;
    int qty;
    double price;
    //在此編寫獲取使用者輸入的程式碼.
    //將資料儲存到InventoryItem物件並返回它
    tempItem.storeInfo(partNum, description, qty, price);
    return tempItem;
}
main 函數隨後應建立 part 語句,如下所示:

InventoryItem part = storeValues();

下面的程式修改了之前的程式,以納入剛才討論的技術。以前名為 storeValues 的函數被重新命名為 createItem,因為它現在會建立一個名為 InventoryItem 的物件並將其返回給 main。showValues 函數現在接收 part 作為常數參照,而不是像以前一樣按值傳遞它。
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

class InventoryItem
{
    private:
        int partNum;    // Part number
        string description;    // Item description
        int onHand;    // Units on hand
        double price;    // Unit price
    public:
        void storeInfo (int p, string d, int oH, double cost);// Prototype
        int getPartNum () const
        {
            return partNum;
        }
        string getDescription() const
        {
            return description;
        }
        int getOnHand() const
        {
            return onHand;
        }
        double getPrice() const
        {
            return price;
        }
};
void InventoryItem::storeInfo(int p, string d, int oH, double cost)
{
    partNum = p;
    description = d;
    onHand = oH;
    price = cost;
}
InventoryItem createItem();    // Returns an Inventoryltem object
void showValues (const InventoryItem&);

int main()
{
    InventoryItem part = createItem ();
    showValues(part);
    return 0;
}
InventoryItem createItem()
{
    InventoryItem tempItem;// Local Inventoryltem object
    int partNum;    // Local variables to hold user input
    string description;
    int qty;
    double price;
    //Get the data from the user
    cout << "Enter data for the new part number n";
    cout << "Part number: ";
    cin >> partNum;
    cout << "Description:";
    cin.get();

    getline(cin, description);
    cout << "Quantity on hand: ";
    cin >> qty;
    cout << "Unit price: ";
    cin >> price;
    tempItem.storeInfo(partNum, description, qty, price);
    return tempItem;
}
void showValues(const InventoryItem &item)
{
    cout << fixed << showpoint << setprecision(2) << endl;
    cout <<    "Part Number : " << item.getPartNum() << endl;
    cout <<    "Description : " << item.getDescription () << endl;
    cout <<    "Units On Hand: " << item.getOnHand () << endl;
    cout <<    "Price : $"<< item.getPrice () << endl;
}
程式輸出結果:

Enter data for the new part number
Part number: 175
Description:Hammer
Quantity on hand: 12
Unit price: 7.49

Part Number : 175
Description : Hammer
Units On Hand: 12
Price : $7.49