当前位置:嗨网首页>书籍在线阅读

08-将引用用于类对象

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

8.2.5 将引用用于类对象

将类对象传递给函数时,C++通常的做法是使用引用。例如,可以通过使用引用,让函数将类string、ostream、istream、ofstream和ifstream等类的对象作为参数。

下面来看一个例子,它使用了string类,并演示了一些不同的设计方案,其中的一些是糟糕的。这个例子的基本思想是,创建一个函数,它将指定的字符串加入到另一个字符串的前面和后面。程序清单8.7提供了三个这样的函数,然而其中的一个存在非常大的缺陷,可能导致程序崩溃甚至不能通过编译。

程序清单8.7 strquote.cpp

// strquote.cpp -- different designs
#include <iostream>
#include <string>
using namespace std;
string version1(const string & s1, const string & s2);
const string & version2(string & s1, const string & s2); // has side effect
const string & version3(string & s1, const string & s2); // bad design
int main()
{
    string input;
    string copy;
    string result;
    cout << "Enter a string: ";
    getline(cin, input);
    copy = input;
    cout << "Your string as entered: " << input << endl;
    result = version1(input, "***");
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;
    result = version2(input, "###");
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;
    cout << "Resetting original string.\n";
    input = copy;
    result = version3(input, "@@@");
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;
    return 0;
}
string version1(const string & s1, const string & s2)
{
    string temp;
    temp = s2 + s1 + s2;
    return temp;
}
const string & version2(string & s1, const string & s2) // has side effect
{
    s1 = s2 + s1 + s2;
// safe to return reference passed to function
    return s1;
}
const string & version3(string & s1, const string & s2) // bad design
{
    string temp;
    temp = s2 + s1 + s2;
// unsafe to return reference to local variable
    return temp;
}

下面是该程序的运行情况:

Enter a string: It’s not my fault.
Your string as entered: It's not my fault.
Your string enhanced: ***It's not my fault.***
Your original string: It's not my fault.
Your string enhanced: ###It's not my fault.###
Your original string: ###It's not my fault.###
Resetting original string.

此时,该程序已经崩溃。

程序说明

在程序清单8.7的三个函数中,version1最简单:

string version1(const string & s1, const string & s2)
{
    string temp;
    temp = s2 + s1 + s2;
    return temp;
}

它接受两个string参数,并使用string类的相加功能来创建一个满足要求的新字符串。这两个函数参数都是const引用。如果使用string对象作为参数,最终结果将不变:

string version4(string s1, string s2) // would work the same

在这种情况下,s1和s2将为string对象。使用引用的效率更高,因为函数不需要创建新的string对象,并将原来对象中的数据复制到新对象中。限定符const指出,该函数将使用原来的string对象,但不会修改它。

temp是一个新的string对象,只在函数version1()中有效,该函数执行完毕后,它将不再存在。因此,返回指向temp的引用不可行,因此该函数的返回类型为string,这意味着temp的内容将被复制到一个临时存储单元中,然后在main()中,该存储单元的内容被复制到一个名为result的string中:

result = version1(input, "***");

将C-风格字符串用作string对象引用参数

对于函数version1(),您可能注意到了很有趣的一点:该函数的两个形参(s1和s2)的类型都是const string &,但实参(input和“**”)的类型分别是string和const char 。由于input的类型为string,因此让s1指向它没有任何问题。然而,程序怎么能够接受将char指针赋给string引用呢?

这里有两点需要说明。首先,string类定义了一种char 到string的转换功能,这使得可以使用C-风格字符串来初始化string对象。其次,本章前面讨论过的类型为const引用的形参的一个属性。假设实参的类型与引用参数类型不匹配,但可被转换为引用类型,程序将创建一个正确类型的临时变量,使用转换后的实参值来初始化它,然后传递一个指向该临时变量的引用。例如,在本章前面,将int实参传递给const double &形参时,就是以这种方式进行处理的。同样,也可以将实参char 或const char *传递给形参const string &。

这种属性的结果是,如果形参类型为const string &,在调用函数时,使用的实参可以是string对象或C-风格字符串,如用引号括起的字符串字面量、以空字符结尾的char数组或指向char的指针变量。因此,下面的代码是可行的:

result = version1(input, "***");

函数version2()不创建临时string对象,而是直接修改原来的string对象:

const string & version2(string & s1, const string & s2) // has side effect
{
    s1 = s2 + s1 + s2;
// safe to return reference passed to function
    return s1;
}

该函数可以修改s1,因为不同于s2,s1没有被声明为const。

由于s1是指向main()中一个对象(input)的引用,因此将s1作为引用返回是安全的。由于s1是指向input的引用,因此,下面一行代码:

result = version2(input, "###");

与下面的代码等价:

version2(input, "###"); // input altered directly by version2()
result = input;           // reference to s1 is reference to input

然而,由于s1是指向input的引用,调用该函数将带来修改input的副作用:

Your original string: It's not my fault.
Your string enhanced: ###It's not my fault.###
Your original string: ###It's not my fault.###

因此,如果要保留原来的字符串不变,这将是一种错误设计。

程序清单8.7中的第三个函数版本指出了什么不能做:

const string & version3(string & s1, const string & s2) // bad design
{
    string temp;
    temp = s2 + s1 + s2;
// unsafe to return reference to local variable
    return temp;
}

它存在一个致命的缺陷:返回一个指向version3()中声明的变量的引用。这个函数能够通过编译(但编译器会发出警告),但当程序试图执行该函数时将崩溃。具体地说,问题是由下面的赋值语句引发的:

result = version3(input, "@@@");

程序试图引用已经释放的内存。