1. 有关返回对象的说明
当成员函数或独立的函数返回对象时,有几种返回方式可供选择。
可以返回指向对象的引用、指向对象的 const引用或 const对象。到目前为止,介绍了前两种方式,但没有介绍最后一种方式,现在是复习这些方式的好时机
1. 返回指向 const对象的引用
使用 const引用的常见原因是旨在提高效率,但对于何时可以采用这种方式存在一些限制。
如果函数返回(通过调用对象的方法或将对象作为参数)传递给它的对象,可以通过返回引用来提高其效率。
例如,假设要编写函数Max(),它返回两个 Vector对象中较大的一个,其中 Vector是第11章开发的一个类。该函数将以下面的方式被使用:
P13-c++使用类-01运算符重载详细介绍,详细的例子演示!
Vector force1(50, 60);
Vector force2(10, 70);
Vector max;
max = Max(force1, force2);
version 1
Vector Max(const Vector &v1, const Vector &v2)
{
if (v1.magval() > v2.magval() ) {
return v1;
} else {
return v2;
}
}
version 2
const Vector & Max(const Vector &v1, const Vector &v2)
{
if (v1.magval() > v2.magval() ) {
return v1;
} else {
return v2;
}
}
这里有三点需要说明。
首先,返回对象将调用复制构造函数,而返回引用不会。
因此,第二个版本所做的工作更少,效率更高。其次,引用指向的对象应该在调用函数执行时存在。
在这个例子中,引用指向force1或 force2,它们都是在调用函数中定义的,因此满足这种条件。第三,v1和v2都被声明为 const引用,因此返回类型必须为 const,这样才匹配。
2. 返回指向非 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。如果使用返回类型 stream,将要求调用 ostream类的复制构造函数,而 ostream没有公有的复制构造函数。幸运的是,返回一个指向cout的引用不会带来任何问题,因为cout已经在调用函数的作用域内。
3. 返回对象
如果被返回的对象是被调用函数中的局部变量,则不应按引用方式返回它,因为在被调用函数执行完毕时,局部对象将调用其析构函数。
例如这里的返回d1
Demo & func(Demo d1)
{
return d1;
}
因此,当控制权回到调用函数时,引用指向的对象将不再存在。
在这种情况下,应返回对象而不是引用。
通常,被重载的算术运算符属于这一类。请看下述示例,它再次使用Vector类
Vector force(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+()能够访问的对象;而返回语句引发的对复制构造函数的隐式调用创建一个调用程序能够访问的对象
4. 返回 const对象
前面的 Vector::operator+()定义有一个奇异的属性,它旨在让您能够以下面这样的方式使用它:
net = force1 + force2 //1: three Vector objects
然而,这种定义也允许您这样使用它:
force1 + force2 = net; //2: dyslectic programming
cout << (forcel + force2 = net).magal() << endl; //3: demented programming
这提出了三个问题。为何编写这样的语句?这些语句为何可行?这些语句有何功能?
首先,没有要编写这种语句的合理理由,但并非所有代码都是合理的。即使是程序员也会犯错。例如,为 Vector类定义 operator==()时,您可能错误地输入这样的代码
if (forcel + force2 = net)
而不是:
if (force1 + force2 == net)
另外,程序员通常很有创意,这可能导致错误
其次,这种代码之所以可行,是因为复制构造函数将创建一个临时对象来表示返回值。因此,在前面的代码中,表达式 force1 + force2的结果为一个临时对象。
在语句1中,该临时对象被赋给net:
在语句2和3中,net被赋给该临时对象。
第三,使用完临时对象后,将把它丢弃。例如,对于语句2,程序计算 force1和 force2之和,将结果复制到临时返回对象中,再用net的内容覆盖临时对象的内容,然后将该临时对象丢弃。原来的矢量全都保持不变。
语句3显示临时对象的长度,然后将其删除。
如果您担心这种行为可能引发的误用和滥用,有一种简单的解决方案:
将返回类型声明为 const Vector
例如,如果 Vector::operator+()的返回类型被声明为 const Vector,则语句1仍然合法,但语句2和语句3将是非法的。
总之,如果方法或函数要返回局部对象,则应返回对象,而不是指向对象的引用。在这种情况下,将使用复制构造函数来生成返回的对象。
如果方法或函数要返回一个没有公有复制构造函数的类(如 ostream
类)的对象,它必须返回一个指向这种对象的引用。
最后,有些方法和函数(如重载的赋值运算符)可以返回对象,也可以返回指向对象的引用,在这种情况下,应首选引用,因为其效率更高。
2. 一个例子 example.cpp
example.cpp
看看返回const 对象是不是允许作为左值。
/*
author:梦悦foundation
公众号:梦悦foundation
可以在公众号获得源码和详细的图文笔记
*/
#include <iostream>
#include <typeinfo>
#include <string>
using namespace std;
class Demo {
private:
int iAge;
string var_name;
public:
Demo() {
iAge = 0;
var_name = "temp";
cout << "Demo(), this(" << this->var_name << "):" << this << ", this->iAge:" << this->iAge << endl;
}
Demo(int i, const string & name = "meng-yue") {
this->iAge = i;
this->var_name = name;
cout << "Demo(int i),this(" << this->var_name << "):" << this << ", i:" << i << ", this->iAge:" << this->iAge << endl;
}
Demo(const Demo & d_copy) {
cout << "Demo(const Demo &d),this(" << this->var_name << "):" << this << ", &d_copy(" << d_copy.var_name << "):" << &d_copy << endl;
}
Demo & operator=(const Demo & d_var) {
cout << "operator=(const Demo&d_var),this(" << this->var_name << "):" << this << ", &d_var(" << d_var.var_name << "):" << &d_var;
cout << ", this->iAge:" << this->iAge << ", d_var.iAge:" << d_var.iAge << endl;
}
~Demo() {
cout << "~Demo,this(" << this->var_name << "):" << this << endl;
}
void Show() {
cout << "Show(),this(" << this->var_name << "):" << this << ", this->iAge:" << this->iAge << endl;;
}
};
Demo func()
{
Demo d1(1, "d1");
cout << "func()" << endl;
return d1;
}
int main()
{
cout << "---------------开始--->公众号:梦悦foundation---------------" << endl;
Demo d2(2, "d2");
func() = d2;
cout << "---------------结束--->公众号:梦悦foundation---------------" << endl;
return 0;
}
首先看上面的代码,不加const的 func 是可以作为左值的
编译运行的结果:
book@book-desktop:~/meng-yue/c++/class_dynamic_memory/04$ ./example
---------------开始--->公众号:梦悦foundation---------------
Demo(int i),this(d2):0xbfb5a670, i:2, this->iAge:2
Demo(int i),this(d1):0xbfb5a668, i:1, this->iAge:1
func()
operator=(const Demo&d_var),this(d1):0xbfb5a668, &d_var(d2):0xbfb5a670, this->iAge:1, d_var.iAge:2
~Demo,this(d1):0xbfb5a668
---------------结束--->公众号:梦悦foundation---------------
~Demo,this(d2):0xbfb5a670
book@book-desktop:~/meng-yue/c++/class_dynamic_memory/04$
可以看到 func() = d2;
这种诡异的操作是可以通过编译,并且成功运行的!
接着我们将func的返回值改成 const Demo看看
const Demo func()
{
Demo d1(1, "d1");
cout << "func()" << endl;
return d1;
}
编译报错了,
book@book-desktop:~/meng-yue/c++/class_dynamic_memory/04$ g++ -o example1 example1.cpp
example1.cpp: In function ‘int main()’:
example1.cpp:54: error: passing ‘const Demo’ as ‘this’ argument of ‘Demo& Demo::operator=(const Demo&)’ discards qualifiers
book@book-desktop:~/meng-yue/c++/class_dynamic_memory/04$
报错的原因是因为 返回的是一个const 对象,他不能调用非const成员函数。所以将operator=() 改成const就可以了。
Demo & operator=(const Demo & d_var) const
编译运行的结果:这样发现,返回一个const对象,也是能够当左值的,难道只有 + 号运算符是这样?
book@book-desktop:~/meng-yue/c++/class_dynamic_memory/04$ g++ -o example1 example1.cpp
book@book-desktop:~/meng-yue/c++/class_dynamic_memory/04$ ./example1
---------------开始--->公众号:梦悦foundation---------------
Demo(int i),this(d2):0xbf999940, i:2, this->iAge:2
Demo(int i),this(d1):0xbf999938, i:1, this->iAge:1
func()
operator=(const Demo&d_var),this(d1):0xbf999938, &d_var(d2):0xbf999940, this->iAge:1, d_var.iAge:2
~Demo,this(d1):0xbf999938
---------------结束--->公众号:梦悦foundation---------------
~Demo,this(d2):0xbf999940
book@book-desktop:~/meng-yue/c++/class_dynamic_memory/04$
example2.cpp
/*
author:梦悦foundation
公众号:梦悦foundation
可以在公众号获得源码和详细的图文笔记
*/
#include <iostream>
#include <typeinfo>
#include <string>
using namespace std;
class Demo {
private:
int iAge;
string var_name;
public:
Demo() {
iAge = 0;
var_name = "temp";
cout << "Demo(), this(" << this->var_name << "):" << this << ", this->iAge:" << this->iAge << endl;
}
Demo(int i, const string & name = "meng-yue") {
this->iAge = i;
this->var_name = name;
cout << "Demo(int i),this(" << this->var_name << "):" << this << ", i:" << i << ", this->iAge:" << this->iAge << endl;
}
Demo(const Demo & d_copy) {
cout << "Demo(const Demo &d),this(" << this->var_name << "):" << this << ", &d_copy(" << d_copy.var_name << "):" << &d_copy << endl;
}
Demo & operator=(const Demo & d_var) const {
cout << "operator=(const Demo&d_var),this(" << this->var_name << "):" << this << ", &d_var(" << d_var.var_name << "):" << &d_var;
cout << ", this->iAge:" << this->iAge << ", d_var.iAge:" << d_var.iAge << endl;
}
const Demo operator+(const Demo &d1) {
cout << "operator+" << endl;
return Demo(this->iAge + d1.iAge);
}
~Demo() {
cout << "~Demo,this(" << this->var_name << "):" << this << endl;
}
void Show() {
cout << "Show(),this(" << this->var_name << "):" << this << ", this->iAge:" << this->iAge << endl;;
}
};
const Demo func()
{
Demo d1(1, "d1");
cout << "func()" << endl;
return d1;
}
int main()
{
cout << "---------------开始--->公众号:梦悦foundation---------------" << endl;
Demo d1(1, "d1");
Demo d2(2, "d2");
Demo d3(3, "d3");
d1 + d2 = d3;
cout << "---------------结束--->公众号:梦悦foundation---------------" << endl;
return 0;
}
我们定义一个 operator+() 函数,它返回const Demo对象,
编译运行结果,即便是这样,发现也能够通过编译,并且运行没有问题。说明这个书写的有问题。
book@book-desktop:~/meng-yue/c++/class_dynamic_memory/04$ ./example2
---------------开始--->公众号:梦悦foundation---------------
Demo(int i),this(d1):0xbfb42af8, i:1, this->iAge:1
Demo(int i),this(d2):0xbfb42af0, i:2, this->iAge:2
Demo(int i),this(d3):0xbfb42ae8, i:3, this->iAge:3
operator+
Demo(int i),this(meng-yue):0xbfb42ae0, i:3, this->iAge:3
operator=(const Demo&d_var),this(meng-yue):0xbfb42ae0, &d_var(d3):0xbfb42ae8, this->iAge:3, d_var.iAge:3
~Demo,this(meng-yue):0xbfb42ae0
---------------结束--->公众号:梦悦foundation---------------
~Demo,this(d3):0xbfb42ae8
~Demo,this(d2):0xbfb42af0
~Demo,this(d1):0xbfb42af8
book@book-desktop:~/meng-yue/c++/class_dynamic_memory/04$
所以,你最好不要这么写。