大家来讨论c++的"函数返回临时对象"

fishsward 2003-09-13 12:23:07
想来想去,总觉得c++的临时对象设计得很不合理.
望高手指教.

问题1.
为什么要设计"临时对象",而不让"函数直接返回对象呢"????
后者的"效率"应该更加高一些吧?
例如:
class A { -----}
A get () { A a;
return a;
}
此时返回的是a的拷贝---临时对象,为什么不直接返回对象a本身呢?
如果担心"函数调用者"直接改变a, 加一个const不就行了吗?
如果考虑的是a的作用域的问题,那么我想说:-----"临时对象"的作用域
也很"短暂"啊?

问题2.
函数返回的"临时对象"为什么设计为"const"呢?
既然是临时对象,很快就会析构的,难道"担心"它被别人改变???????
相反,如果不设计为const,应该更加"爽"一些吧?
void f ( A & a) {---------------}
void main () { f(get()); }
它不能通过编译的"罪魁祸首"就是因为get()返回的临时对象是const!!!


问题3
不知这是vs.net2003 的问题,还是其他的问题??
A & a1= get();// 我原本猜测它应该不能通过编译的(因为a1不是const),
但是令我吃惊的是它竟然可以通过编译,并且结果
调用了--------两次构造函数,一次析构函数!!!!!!

const A & a2=get()
//C++标准:如果一个临时对象被赋值给一个引用,
这个临时对象在这个引用的生命周期中将不能被销毁。
换句话说,不同于返回一个局部变量的引用,将一个
引用绑定到一个临时对象上是完全合法的,任何时候
使用这个引用,这个对象都应该是有效的.
------令我吃惊的是---------:
还是调用--------两次构造函数,一次析构函数!!!!!!


欢迎指教!!



...全文
408 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
GhostAdz 2003-09-15
  • 打赏
  • 举报
回复
关于问题1
应该是局部变量要销毁的 不能带到外部去
所以要先构造一个临时变量存放
当然除了RVO (named return value optimization)
其他问题建议楼主看primer上的关于返回问题 查找return value optimization就可以(电子班)
或者看more effective c++
fishsward 2003-09-15
  • 打赏
  • 举报
回复
xie xie
JoeRen 2003-09-14
  • 打赏
  • 举报
回复
另一个问题,为什么你的程序调用了两次构造函数,一次析构函数呢?原因就是你的A::get()函数:
A A::get(){ A a; return a; }
语义上,这段代码说的是:

声明一个类型为 A的局部变量a。(隐含地调用一次构造函数)
将*a的值*返回调用者。

注意,返回的是a的值。由于a是个局部变量,因此必须在退出函数前被析构。因为有些类是设计用来处理成对操作的(如临界区操作),如果你不释放A,那些类的成对操作就不能保证。这样,就必须在析构前将A的值拷贝到一个临时变量。而这个临时变量也是需要构造的,因此构造函数被调用两次。

要避免两次构造和一次析构,这样写:
A A::get(){ return A(); }
这样只会调用一次构造函数。

另外,关于VC.NET的问题,我认为这是一个提高性能的好方法。返回的变量定义为引用,使用上完全跟原始类型一样,但免去了一次复制构造函数的调用。当然定义为常引用也有此好处,但就不能修改其值了。
JoeRen 2003-09-14
  • 打赏
  • 举报
回复
++运算符决不是只能返回const &的。以下代码在Dev-C++上通过
class A
{
int i;
public:
A(){i=0;}
A &operator ++(){ ++i;return *this; }
};

int main()
{
A a;
++a;
system("pause");
}

但如果将返回值改成const &的话,就不能写出类似于++(++a)的代码了。确实需要避免这种用法的时候就用const &好了。
cenphoenix 2003-09-13
  • 打赏
  • 举报
回复
mark 学东西!!!
fishsward 2003-09-13
  • 打赏
  • 举报
回复
up
zhukeke 2003-09-13
  • 打赏
  • 举报
回复
1. class A { -----}
A get () { A a;
return a;
};
对象a分配在栈中,函数返回时已经析构,是不可返回的。因此需要创建“临时对象”。

2.在函数的调用者接收了函数返回的“临时对象”时,如果是作为引用接收的,此时该“临时对象”即转变为“永久对象”,不再析构。如果是作为非引用接收,则c++将再创建一个copy对象copy这一“临时对象”,然后将该“临时对象”析构。你的问题2、3都属于这一问题。

hslinux 2003-09-13
  • 打赏
  • 举报
回复
1) 为什么要设计"临时对象",而不让"函数直接返回对象呢"????
后者的"效率"应该更加高一些吧~~~~~~~~`

如果这样,那么 作用域 的范围就没办法确定了。
fishsward 2003-09-13
  • 打赏
  • 举报
回复
to hslinux(幻世龙):
临时对象的也是在"栈"中创建的,它的自动析构就是证据!!
既然都是在"栈"中,并且编译器能够保证"临时对象"的生存期,那么也就可以想
办法保证"直接返回的对象"的生存期!!!

to cnswdevnet(跟皮虫):
你的解释的确可以解决我的很多疑惑.
不过,我在Think in c++,以及很多杂志书刊中都看到"临时对象"是const的
另外,你可以参考"++操作符的重载",其中,为什么"前缀++"和"后缀++"都返回的是
"const &"?? 就是因为"临时对象"必须是"const"造成的!

欢迎两位以及更多的朋友参加讨论!!!
cnswdevnet 2003-09-13
  • 打赏
  • 举报
回复
3. vs.net 2003 的问题.

2. 谁说临时对象是const的????? 这纯属胡说八道. 临时对象是可以改变的(但必须是通过自己的成员函数), 例:
class A
{
int a;
public:
void f() { a = 1; }
};

A f() { A a; return a; }

int main()
{
f().f();
}

这个程序完全合法.
临时对象必须用 常引用 附着的原因一是因为非 常引用 只能附着左值, 不能附着右值, (如果能附着右值, 那右值物件就完全可变为左值物件来用, 那就乱套了), 只有用常引用附着临时对象才不会引起混乱. 另外, 这一规定可以减少很多麻烦(和产生bug的机会), 比如:
class A
{
public:
A( int );
};

void f( A& );

int main()
{
f( 1 ); // *
}

注意 带 * 的语句, 显然, 如果标准没有这个规定, 这个语句是通过的, 因为 1 可以引发一个A的临时物件, 而A&又可以接受临时物件. 这再绝大多数情况下, 不是我们想要做的: 这里的f显然是想加工传递进来的A物件的值, 然后这个加工后物件可被调用者使用, 如果传递进来的是一个不可能被调用者使用的临时物件, 这个调用则无意义. 从这个简单的分析可以看出, 如果没有这个规定, 我们一定又有很多写出buggy代码的机会.

1. 函数体里auto物件在最后一个}时生命全部结束, 要想把值传递到外部, 必须通过一个临时的拷贝. 这是很干净的语义. 如果按你说的, 直接传递一个auto物件, 那语义又乱套了. 计算机语言就是一个逻辑系统, 保证语义的完整和自恰是必须的. 这里的规定正是因为要满足这一语义要求, 与临时对象的生命长度没关系.

64,646

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧