免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 5395 | 回复: 21
打印 上一主题 下一主题

请教大家一个问题:关于临时变量的“const”属性 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-08-16 11:36 |只看该作者 |倒序浏览
在著名的“c++编程思想”这本书中,第七章《常量》里面,对于临时变量谈到了这样一个观点:“它们自动地成为常量”(在第8页)。我个人感觉这是个比较合理的解释。在书中的第10页还举了一个例子证明,代码如下:
#include        <stdio.h>
#include        <iostream>
#include        <fstream>
#include        <backward/strstream>
#include        <cassert>
#include        <exception>

using namespace std;

class X { };

X f() { return X(); }

void g1(X&) { }
void g2(const X&) { }

int
main(void)
{

        g1(f());          // A temporary object will be created here, we name it with 'T1'
        g2(f());

        return(0);
}


在我用的AIX服务器上,编译这段程序时会如期望的那样报错:
a.cpp: In function `int main()':
a.cpp:21: error: invalid initialization of non-const reference of type 'X&'
   from a temporary of type 'X'
a.cpp:14: error: in passing argument 1 of `void g1(X&)'

我们假设f()产生一个临时对象T1,而T1具有const属性,所以你不能把它作为一个非const引用所指向的对象。因为这样可能会破坏const的常量性限定,所以编译器禁止此做法。

但是我们知道,如果一个对象具有const属性,那么它的特征之一就是只能调用它的const成员函数。那么据此判断,如果上例中的类X有成员函数,那么临时变量T1就只能调用其中的const成员函数。真的是这样吗,我把书里面的代码稍微改了一下:
#include        <stdio.h>
#include        <iostream>
#include        <fstream>
#include        <backward/strstream>
#include        <cassert>
#include        <exception>

using namespace std;

class X {
        int i;
public:
        X (int I=100) : i(I) { }
        void Print_X() {
                i++;
                cout << "Now i is " << i << endl;
        }
};

X f() { return X(); }

int
main(void)
{

        f().Print_X();      // A temporary object will be created here, we name it with 'T1'

        return(0);
}

大家猜猜编译的结果是什么?顺利通过!执行编译好的程序,输出如下:
Now i is 101
可以看到,不仅非const成员函数调用成功,而且临时对象T1的成员值实实在在地被改变了!也就是说,从这个例子看,T1并不具备const属性。

两个程序得到了两个相反的结论。我的感觉是:对于一个临时对象,当你把它和其它的对象发生联系时(比如你把这个临时变量的地址赋给一个指针,或者你把这个临时变量作为另一个引用所指向的对象),它的const限定才会起作用;但如果它不和其它的对象发生关系,似乎不去关心它是否有const属性。

这只是我的猜测,没什么理论根据。非常希望能听听大家的意见,更希望能有高手给一个明确的答复。谢谢!

[ 本帖最后由 羽人 于 2006-8-16 14:22 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2006-08-16 11:54 |只看该作者
???
没有看明白lz的程序
希望给出一个整洁一些的代码

论坛徽章:
0
3 [报告]
发表于 2006-08-16 11:59 |只看该作者
以我理解, const 的變數一但初始化後就不能改變它的值的.
不知道我理解有沒有錯呢? thx

论坛徽章:
0
4 [报告]
发表于 2006-08-16 14:23 |只看该作者
原帖由 kuaizaifeng 于 2006-8-16 11:54 发表
???
没有看明白lz的程序
希望给出一个整洁一些的代码


不好意思,没留神把一些垃圾代码放到里面了。
现在好了。

论坛徽章:
0
5 [报告]
发表于 2006-08-16 14:36 |只看该作者
原帖由 惠繪洋 于 2006-8-16 11:59 发表
以我理解, const 的變數一但初始化後就不能改變它的值的.
不知道我理解有沒有錯呢? thx

不知道你所说的“變數”是不是指的对象内的成员,比如我所举例子中的X中的i?

如果是的话,我理解你这种说法并不完全正确,虽然在大多数情况下是正确的。
因为c++里还有一个“mutable”关键字,如果你把类里的某一个成员声明成mutable类型,那么在一个const对象里它的值是可以被改变的。但是也只能在const函数里被改变,因为一个const对象是不准调用非const成员函数的。举个例子(只是把之前的例子修改了一下):
#include        <stdio.h>
#include        <iostream>
#include        <fstream>
#include        <backward/strstream>
#include        <cassert>
#include        <exception>

using namespace std;

class X {
        mutable int i;
public:
        X (int I=100) : i(I) { }
        void Print_X() const {
                i++;
                cout << "Now i is " << i << endl;
        }
};

int
main(void)
{
        const X x;

        x.Print_X();
        x.Print_X();
        x.Print_X();
        x.Print_X();

        return(0);
}

这个程序的输出是:
Now i is 101
Now i is 102
Now i is 103
Now i is 104

所以一个const对象的内容也是可以被修改的,但是要利用关键字mutable。

论坛徽章:
0
6 [报告]
发表于 2006-08-16 17:24 |只看该作者
临时变量自动成为const,应该是在临时变量做为参数的时候吧。
在f()中,构造出一个临时变量,然后又做为g1()的参数时,就自动具有了const属性
但是f()返回的临时变量,本身是不具备const属性的,否则,应该这样声明f()
const X  f() {   /* ......*/  }

论坛徽章:
0
7 [报告]
发表于 2006-08-16 18:09 |只看该作者
原帖由 namtso 于 2006-8-16 17:24 发表
临时变量自动成为const,应该是在临时变量做为参数的时候吧。
在f()中,构造出一个临时变量,然后又做为g1()的参数时,就自动具有了const属性
但是f()返回的临时变量,本身是不具备const属性的,否则,应该这样 ...


谢谢你的回复!

但是我对你所说的内容,有一些小小的疑问,想和你探讨一下。还是拿例子中所说的f()来做说明吧:
X f() { return X(); }

首先,我认可你所说的“在f()中,构造出一个临时变量”。我认为应该是“return X()”语句产生这个临时变量,而且具有const属性。我们姑且给它命名为T2。但是传给g1()函数的变量是T2吗?按常理来讲,一旦f()函数的调用结束,临时变量T2的作用域就消失了。所以对于下面这条语句:
g1(f());
我认为应该产生一个新的临时变量T1,T1的值来自函数f()的返回值T2,是通过系统默认的拷贝构造函数来赋给T1的。T1才是传递给g1()函数的变量。

第二点,也是最令我迷惑的:你也说了,这两种情况都是产生临时变量。那是什么原因使得一个临时变量具有const属性,而另一个临时变量却没有呢?

[ 本帖最后由 羽人 于 2006-8-16 18:11 编辑 ]

论坛徽章:
0
8 [报告]
发表于 2006-08-16 21:47 |只看该作者
楼主的第二个程序已经证明了关于临时对象“自动地成为常量”这一命题是错误的。如果没有 const 的限制,通常临时对象如果是一个类对象的话,是可以被改变的,不是常量对象。

至于为什么对临时对象的引用必须要进行 const 引用要从另一个方面来解释,并不是因为临时对象是一个常量对象。

当然了,对于常量或常量对象只能进行 const 引用,这是毋庸置疑的。然而,即使一个对象不是常量对象,但如果它是一个右值的话,对于这样的对象也只能进行 const 引用,不能进行非 const 引用。程序中的 f() 返回的是一个右值对象,就属于这种情况。

论坛徽章:
0
9 [报告]
发表于 2006-08-17 08:43 |只看该作者
原帖由 whyglinux 于 2006-8-16 21:47 发表
楼主的第二个程序已经证明了关于临时对象“自动地成为常量”这一命题是错误的。如果没有 const 的限制,通常临时对象如果是一个类对象的话,是可以被改变的,不是常量对象。

至于为什么对临时对象的引用必须要 ...


太感谢了!
真是一语道破啊!
也许正是因为这个原因,所以我们看到的拷贝构造函数都是“X(const X& x)”的形式。如果去掉参数中的const限定,编译器就会报错。

看来牛人 Bruce Eckel 也有犯错误的时候。
不过还有一个问题想请教 whyglinux ,请问您是怎么定义或者说区分“左值”和“右值”的?谢谢!

论坛徽章:
0
10 [报告]
发表于 2006-08-17 20:09 |只看该作者
>> 请问您是怎么定义或者说区分“左值”和“右值”的?

对于函数的返回值来说这个问题比较简单。如果函数返回类型是一个引用,则是左值;其它情况的函数返回都是右值。

关于左值和右值更具体的情况可搜索一些资料来研究。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP