c++临时对象大剖析?

winnuke 2009-11-18 02:04:25
关键字:l-value、 r-value、 const value、temporary and it's

scope


前几天有人发现这样一段代码在g++和vc6下编译结果不同
http://topic.csdn.net/u/20091113/19/ec6af899-cb1a-4a81-92b7-0ab2bc8c2f24.html

其中一个重要问题就是临时对象的问题。
之后自己对临时对象这个以前很少注意到的东西越想越投入,而且发现这个

东东似乎比构造、析构、拷贝构造、赋值等c++的一些自动化特性更多地被应

用在代码中且各大编译器对此的支持程度不一。
目前有几个问题到现在还不是很清楚,特列出来请大家指点:

1:临时对象在什么情况下产生?标准有没有规定?
2:临时对象的作用域或者生命周期是怎么样的?
3:临时对象到底是不是右值?
4:如果临时对象作为一个右值,可不可以修改?或者说右值是不是const或

者常量?
5:右值是否不可以被关联到非常量(const)引用?为什么?

请大神小虾们各抒己见。先写个类以供大家讨论使用:

#include <iostream>
using std::cout;
using std::endl;

class A
{
public:
A(int i)
: m_i(i)
{
cout << "A(" << m_i << ")" << endl;
}

~A()
{
cout << "~A(" << m_i << ")" << endl;
m_i = 0;
}

A(const A& a)
: m_i(a.m_i)
{
cout << "A(A(" << m_i << "))" << endl;
}

A& operator=(const A& rhs)
{
cout << "A(" << m_i << ")" << "=A(" << rhs.m_i << ")" << endl;
m_i = rhs.m_i;
return *this;
}

A& operator--()
{
cout << "--(" << m_i << ")" << endl;
--m_i;
return *this;
}

private:
int m_i;
};

A GetA(int i)
{
A a(i);
return a;
}

int main()
{
A la1(1);
A ra2 = --GetA(2);
--ra2;
A ra3 = GetA(3);
A ra4 = GetA(4);
A* pa = &GetA(5);
return 0;
}

...全文
476 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
Jinhao 2009-11-19
  • 打赏
  • 举报
回复
5:右值是否不可以被关联到非常量(const)引用?为什么?
c++标准规定右值不能被非常量引用关联。大概是出于为了避免一些逻辑错误而做出该规定。
但是在vc6、9中一些右值是可以关联到非const引用的,这难道是vc的扩展还是其bug?
----
这是编译器的问题。。。
winnuke 2009-11-19
  • 打赏
  • 举报
回复
总结一下先:
1:临时对象在什么情况下产生?标准有没有规定?
目前知道的情况有表达式、函数实参传递时直接写在参数列表里的、或者间接构造的实参、函数返回非引用的时候均可能产生。

2:临时对象的作用域或者生命周期是怎么样的?
如果该对象被引用,则生命期延长到引用的生命周期,否则在表达式一结束就个屁。

3:临时对象到底是不是右值?
是。

4:如果临时对象作为一个右值,可不可以修改?或者说右值是不是const或者常量?
Jinhao的说法是非pod类型的右值可以修改。其他则不可以。(这里涉及到pod类型的定义问题,以后再开帖讨论)
我的想法是如果该右值对象有方法则可以调用其方法。而内置数据类型是没有方法的。
但是不管怎么样,确实存在一些临时对象(右值)是可以通过调用其成员函数的,可以使const函数或者非const函数。

5:右值是否不可以被关联到非常量(const)引用?为什么?
c++标准规定右值不能被非常量引用关联。大概是出于为了避免一些逻辑错误而做出该规定。
但是在vc6、9中一些右值是可以关联到非const引用的,这难道是vc的扩展还是其bug?
陌上花花 2009-11-18
  • 打赏
  • 举报
回复
学习下
lovesi3344 2009-11-18
  • 打赏
  • 举报
回复
up
jeff_nie 2009-11-18
  • 打赏
  • 举报
回复
mark
yutaooo 2009-11-18
  • 打赏
  • 举报
回复

啊~~真好~~这个帖子~
dragonzcs 2009-11-18
  • 打赏
  • 举报
回复
嗯,右值应该只是相对的吧,没有什么固定的标准。。。
winnuke 2009-11-18
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 dragonzcs 的回复:]
临时对象貌似并非常量,如:
C/C++ codestring foo()
{string str="Hello!";return str;
}int main()
{string str="world!";
foo()=str;//可以改变临时变量return0;
}
[/Quote]

我现在的理解就是类型为内置类型的右值不能被改变。但是如果该右值为一个类对象,可以调用该对象的方法。
这里返回的是一个右值,调用的是string的operator=方法。
逸学堂 2009-11-18
  • 打赏
  • 举报
回复
又是一个大问题~
dragonzcs 2009-11-18
  • 打赏
  • 举报
回复
临时对象貌似并非常量,如:

string foo()
{
string str="Hello!";
return str;
}

int main()
{
string str="world!";
foo()=str;//可以改变临时变量
return 0;
}
super_chris 2009-11-18
  • 打赏
  • 举报
回复
如果这样返回:
return integer(a+b);
就产生一个临时对象。编译器通常会优化将此对象直接创建到接收函数返回处,书上说的。
LZ你的很多疑问也是我的疑问,UP。
[Quote=引用 5 楼 winnuke 的回复:]
1:临时对象在什么情况下产生?标准有没有规定?
2:临时对象的作用域或者生命周期是怎么样的?
3:临时对象到底是不是右值?
4:如果临时对象作为一个右值,可不可以修改?或者说右值是不是const或者常量?
5:右值是否不可以被关联到非常量(const)引用?为什么?

回1楼Jinhao:
1:除了这种计算表达式之外是否还有其他情况会导致临时对象产生?据我所知函数除了返回一个引用之外都会产生一个临时对象。
2:这个问题我不能肯定。显然作为函数参数传递时产生的临时对象作用域是整个函数。
3:我支持,但不能肯定。
4:请问类A是不是pod类型?


[/Quote]
Jinhao 2009-11-18
  • 打赏
  • 举报
回复
具体会不会生成临时对象由编译器决定,有些临时对象会被优化掉。
对于第二点,最常用的一个方法就是

void print_string(const std::string& str)
{
cout<<str<<endl;
}

std::string foo() { return "string"; }

print_string(foo());
print_string("abc"); //这两个调用parameter都是引用的临时对象。
winnuke 2009-11-18
  • 打赏
  • 举报
回复
呵呵,知道一个重要特性右值一旦被引用其生命周期(作用域)会被延长到引用的作用域。否则语句返回即可作废。
Jinhao 2009-11-18
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 winnuke 的回复:]
C/C++ codeclass PodB
{public:int m_1;int m_2;void Set(int i1,int i2)
{
cout<<"Set("<< i1<<","<< i2<<")"<< endl;
m_1= i1;
m_2= i2;
}int sum()const
{
cout<< m_1<<"+"<< m_2<<"="<< m_1+ m_2<< endl;return m_1+ m_2;
}
};int main()
{
PodB().Set(3,4);// 按理这里是pod类型则不应该被修改,编译应该错误。但是vc9是正确的return0;
}
[/Quote]

类不是POD
Jinhao 2009-11-18
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 pengzhixi 的回复:]
引用 3 楼 jinhao 的回复:
2:临时对象的作用域或者生命周期是怎么样的?
单个语句. 如果临时对象邦定到一个const引用,生命期则延长到跟该引用被销毁


感觉还与编译器的处理有点关系
[/Quote]

没关系,是语言定义的。
winnuke 2009-11-18
  • 打赏
  • 举报
回复
class PodB
{
public:
int m_1;
int m_2;

void Set(int i1, int i2)
{
cout << "Set(" << i1 << "," << i2 << ")"<< endl;
m_1 = i1;
m_2 = i2;
}
int sum() const
{
cout << m_1 << "+" << m_2 << "=" << m_1 + m_2 << endl;
return m_1 + m_2;
}
};

int main()
{
PodB().Set(3, 4); // 按理这里是pod类型则不应该被修改,编译应该错误。但是vc9是正确的
return 0;
}
pengzhixi 2009-11-18
  • 打赏
  • 举报
回复
标准是没有对临时对象有什么规定的
Jinhao 2009-11-18
  • 打赏
  • 举报
回复
1:除了这种计算表达式之外是否还有其他情况会导致临时对象产生?据我所知函数除了返回一个引用之外都会产生一个临时对象。
--
你把所有的操作符都想成是函数就行了。只有返回引用不会创建临时对象。


第二个问题,我在3楼补充了一点。

class T
{
public:
T(int a):i(a){}
~T(){ i = 0; }

void print() const
{
cout<<i<<endl;
}
private:
int i;
};

T foo()
{
return T(5);
}
int main()
{

const T& r = foo();


r.print(); //r引用的临时对象仍然有效。
}


第三点,无须质疑

4:请问类A是不是pod类型?
POD就是语言定义的那些整数类型,指针类型,例如int,char, int *,void(*)()...

5: 右值是否不可以被关联到非常量(const)引用?为什么?
不可以,语言定义。non-const-ref不会像const-ref那样延长临时对象的生命。

判断是不是右值,需要从表达式的语义上判断,而不是从形式上判断。
如果你有兴趣可以看看
http://topic.csdn.net/u/20090706/16/514af7e1-ad20-4ea3-bdf0-bfe6d34d9814.html
do_fork 2009-11-18
  • 打赏
  • 举报
回复
Arithmetic types (3.9.1), enumeration types, pointer types, and pointer to
member types (3.9.2), and cv-qualified versions of these types (3.9.3) are
collectively called scalar types. Scalar types, POD-struct types, POD-union
types (clause 9), arrays of such types and cv-qualified versions of these
types (3.9.3) are collec-tively called POD types.
winnuke 2009-11-18
  • 打赏
  • 举报
回复
引用:
你可以将 POD 类型看作是一种来自外太空的用绿色保护层包装的数据类型,POD 意为“Plain Old Data”(译者:如果一定要译成中文,那就叫“彻头彻尾的老数据”怎么样!)这就是 POD 类型的含义。其确切定义相当粗糙(参见 C++ ISO 标准),其基本意思是 POD 类型包含与 C 兼容的原始数据。例如,结构和整型是 POD 类型,但带有构造函数或虚拟函数的类则不是。 POD 类型没有虚拟函数,基类,用户定义的构造函数,拷贝构造,赋值操作符或析构函数。
为了将 POD 类型概念化,你可以通过拷贝其比特来拷贝它们。此外, POD 类型可以是非初始化的。
加载更多回复(6)

64,662

社区成员

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

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