woaidongmao

文章均收录自他人博客,但不喜标题前加-[转贴],因其丑陋,见谅!~
随笔 - 1469, 文章 - 0, 评论 - 661, 引用 - 0
数据加载中……

猜猜看,id变成9了吗?

#include "stdafx.h"
#include "stdio.h"
struct TestStr
{
    int id;
    void SetId(int id_new){id = id_new;} 
};

int main(int argc, char* argv[])
{
    const TestStr ts = {1};
    const TestStr* p_ts = &ts;
    ((TestStr)(*p_ts)).SetId(9);
    printf("TestStr::id = %d\r\n",ts.id);
    return 0;
}


id还是等于1,不信试试,顺便替换成下面这一句再试试看
((TestStr&)(*p_ts)).SetId(9);


另外还有一个奇怪的问题:
((TestStr)(*p_ts)).SetId(9);      //编译通过
((TestStr)(*p_ts)).id = 9;         //编译不通过
(&((TestStr)(*p_ts)))->id = 9; //编译通过

谁可以解释一下原因?不知。

posted on 2008-04-01 22:03 肥仔 阅读(1622) 评论(10)  编辑 收藏 引用 所属分类: C++ 基础

评论

# re: 猜猜看,id变成9了吗?  回复  更多评论   

((TestStr)(*p_ts)).SetId(9); 如果变成(TestStr(*p_ts)).SetId(9);
这其实相当于调用了TestStr(const TestStr&)生成了一个TestStr&的临时变量。
按理来说这三个应该都可以,确实在gcc 4.1.3下可以全部编译通过的。
这个可能和编译器有关,在编译((TestStr)(*p_ts)).id = 9; 时生成的临时变量可能为const的,所以编译不过。
2008-04-01 23:18 | www

# re: 猜猜看,id变成9了吗?  回复  更多评论   

@www
//((TestStr)(*p_ts)).SetId(9); 如果变成(TestStr(*p_ts)).SetId(9);
((TestStr)(*p_ts)).SetId(9); 和(TestStr(*p_ts)).SetId(9); 是一样的,//上面写错了
2008-04-01 23:20 | www

# re: 猜猜看,id变成9了吗?  回复  更多评论   

C++标准规定,类性转换表达式的目标类型为引用时,结果为l-value;否则为r-value

对于built-in 类型,r-value是不可修改的;而对于user-defined类型,r-value在某些情况下是允许修改的

GCC编译没问题,m$对标准的支持存在问题吧
2008-04-02 00:01 | 啸天猪

# re: 猜猜看,id变成9了吗?  回复  更多评论   

关于结果等于1的问题,((TestStr)(*p_ts)) 新生成了一个对象,因为p_ts指向的是const
2008-04-02 09:27 | hsen

# re: 猜猜看,id变成9了吗?  回复  更多评论   

((TestStr)(*p_ts)).SetId(9); //编译通过
((TestStr)(*p_ts)).id = 9; //编译不通过
(&((TestStr)(*p_ts)))->id = 9; //编译通过


我在VS2005下:
正如作者所说中间的是编不过的,error,说是l-value不能赋值。
但是能编过的2个结果都还是1,没有修改了原来的值,因为在类型转化的时候都调用了拷贝构造函数,从新生成一个对象你修改的是拷贝后的临时对象。(你可以写拷贝构造函数测试一下)


所以同意:
这其实相当于调用了TestStr(const TestStr&)生成了一个TestStr&的临时变量。
C++标准规定,类性转换表达式的目标类型为引用时,结果为l-value;否则为r-value。


2008-04-02 10:02 | 梦在天涯

# re: 猜猜看,id变成9了吗?  回复  更多评论   

类型转换(非引用)必然会产生一个临时对象。所有的一切问题都可以归结到这里:
((TestStr)(*p_ts)).SetId(9);//产生non-const临时对象,SetId()操作的是临时对象,因此原对象不变
((TestStr&)(*p_ts)).SetId(9);//去掉原对象的const。可以写成const_cast<TestStr&>(*p_ts)).SetId(9)
((TestStr)(*p_ts)).id = 9; //GCC可以编译
(&((TestStr)(*p_ts)))->id = 9; //编译通过
2008-04-02 13:57 | raof01

# re: 猜猜看,id变成9了吗?  回复  更多评论   

@梦在天涯
这个有道理,有人验证过吗?
2008-04-02 16:16 | foobar

# re: 猜猜看,id变成9了吗?  回复  更多评论   

你看下你的反汇编代码就知道了。
编译的时候,常量在允许的情况下编译器会帮你替换成立即数(因为立即寻址比较快)。
所以你的printf("TestStr::id = %d\r\n",ts.id)在编译的时候会当成
printf("TestStr::id = %d\r\n",1)编译。
但是ts所在内存的内容是的确已经改变了。
你把(*p_ts)打出来看看就知道了。
对于指针,编译器是没有办法在编译的时候替换成立即数的。
2008-04-02 19:52 | -.-:

# re: 猜猜看,id变成9了吗?  回复  更多评论   

把void SetId(int id_new)的参数改成引用或指针类型试试?


2008-04-08 17:45 | ww

# re: 猜猜看,id变成9了吗?  回复  更多评论   

((TestStr)(*p_ts)).SetId(9); //编译通过
((TestStr)(*p_ts)).id = 9; //编译不通过
(&((TestStr)(*p_ts)))->id = 9; //编译通过


1,3 大家都讲了,这个很好理解,实际上是生成了临时的对象。编译通过但是不会修改原来的变量。
2 可以通过这个例子来理解

class A;
Func(A& p) {...}
main()
{
Func(A());//编译错误,因为临时对象必须是const&
}

如果Func(A const&p) {A &c =(A&)p;c.xxx}这是合法的。
所以3,就类似与后面这个Func 作的事情。

临时对象不能修改,这是c++标准规定的,在侯捷书More C++ Exception里面提到过的。至于g++可以编译通过,G++在默认拷贝时候构造的对象多于1个造成的。大家可以在析构函数里输出就可以看出来了。
2008-12-20 23:05 | 杨成

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理