有关返回对象的说明

返回指向const对象的引用

使用const引用的常见原因是旨在提高效率,但对于何时可以采用这种方式存在一些限制。如果函数返回(通过调用对象的方式或将对象作为参数)传递给它的对象,可以通过返回引用来提高其效率。假设要编写函数Max(),它返回两个Vector对象中较大的一个,该函数将以下面方式被使用:

Vector force1(50, 60);
Vector force2(10, 70);
Vector max;
max = Max(force1, force2);

下面两种实现都是可行的

//版本1
Vector Max(const Vector &v1, const Vector &v2)
{
    
    
	if (v1.magval() > v2.magval())
		return v1;
	else
		return v2;
}
//版本2
const Vector & Max(const Vector &v1, const Vector &v2)
{
    
    
	if (v1.magval() > v2.magval())
		return v1;
	else
		return v2;
}

1)返回对象将调用复制构造函数,而返回引用不会。因此,第二个版本所做的工作更少,效率更高。
2)引用指向的对象应该在调用函数执行时存在。这个例子中,引用指向force1或force2,它们都是在调用函数中定义的,因此满足这种条件。
3)v1和v2都被声明为const引用,因此返回类型必须为const ,这样才匹配。

返回指向非const对象的引用

两种常见的返回非const对象情形,重载赋值运算符以及重载与cout一起使用的<<运算符。前者这样做为了提高效率,而后者必须这样做。

operator=()的返回值用于连续赋值:

String s1("Good stuff");
String s2, s3;
s3 = s2 = s1;

s2.operator=()的返回值被赋给s3。为此,返回String对象或String对象的引用都是可行的,但与Vector示例一样,通过使用引用,可避免该函数调用String的复制构造函数来创建一个新的String对象。在这个例子,返回类型不是const,因为方法operator=()返回一个指向s2的引用,可以对其进行修改。

Operator<<()的返回值用于串接输出:

String s1("Good stuff");
cout << s1 << "is coming!";

operator<<(cout, s1)的返回值成为一个用于显示字符串"is coming!"的对象。返回类型必须是ostream&,而不能仅仅是ostream。如果使用返回类型ostream,将要求调用ostream类的复制构造函数,而ostream没有公有的复制构造函数。返回一个指向cout的引用不会带来任何问题,因为cout已经在调用函数的作用域内。

返回对象

如果被返回的对象是被调用函数中的局部变量,则不应按引用方式返回它,因为在被调用函数执行完毕时,局部对象将调用其析构函数。因此,当控制权回到调用函数时,引用指向的对象将不再存在。这种情况,应返回对象而不是引用。通常,被重载的算术运算符属于这一类。

Vector force1(50, 60);
Vector force2(10, 70);
Vector net;
net = force1 + force2;

返回的不是force1,也不是force2,force1和force2在这个过程中应该保持不变。因此,返回值不能是指向在调用函数中已经存在的对象的引用。相反,在Vector::operator+()中计算得到的两个矢量的和被存储在一个新的临时对象中,该函数也不应返回指向该临时对象的引用,而应该返回实际的Vector对象,而不是引用:

Vector Vector::operator+(const Vector &b) const
{
    
    
	return Vector(x + b.x, y + b.y);
}

在这样情况下,存在调用复制构造函数来创建被返回的对象的开销,然而这是无法避免的。

构造函数调用Vector(x+b.x, y+b.y)创建一个方法operator+()能够访问的对象;而返回语句引发的对复制构造函数的隐式调用创建一个调用程序能够访问的对象。

返回const对象

Vector::operator+()定义有一个奇异的属性:

net = force1 + force2; //1:3个Vector对象

然而,也可以这样使用:

force1 + force2 = net; //2:阅读障碍的编程
cout << (force1 + force2 = net).magval() << endl; //3:疯狂的编程

1)没有要编写这种语句的合理理由,但并非所有代码都是合理的。即使是程序员也会犯错。Vector类定义operator==()时,可能错误输入这样的代码:

if(force1 + force2 = net)

而不是:

if(force1 + force2 == net)

另外,程序员通常很有创意,可能导致错误。
2)这种代码可行,是因为复制构造函数将创建一个临时对象来表示返回值。因此,在前面的代码中,表达式force1+force2的结果为一个临时变量。在语句1中,该临时对象被赋给net;在语句2和3中,net被赋给该临时对象。
3)使用完临时对象后,将把它丢弃。对于语句2,程序计算force1和force2之和,将结果复制到临时返回对象中,再用net的内容覆盖临时对象的内容,然后将该临时对象丢弃。原来的矢量全都保持不变。语句3显示临时对象的长度,然后将其删除。
4)如果担心这种行为可能引发误用和滥用,解决方法:将返回类型声明为const Vector。如果Vector::operator+()的返回类型被声明为const Vector,则语句1仍然合法,但语句2和语句3将是非法的。
5)如果方法或函数要返回局部对象,则应返回对象,而不是指向对象的引用。在这种情况下,将使用复制构造函数来生成返回的对象。如果方法或函数要返回一个没有公有复制构造函数的类(ostream类)的对象,它必须返回一个指向这种对象的引用。最后,有些方法和函数(重载的赋值运算符)可以返回对象,也可以返回指向对象的引用,在这种情况下,应首选引用,效率更高。

猜你喜欢

转载自blog.csdn.net/qq_36314864/article/details/119773058
今日推荐