C++类对象作为函数参数传递详解

我们知道了如何使用变量作为函数的实参,类对象也可以作为实参传递给函数

例如,以下函数具有一个接收 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