const传奇

[精华] const传奇


http://www.chinaunix.net 作者:HopeCao  发表于:2004-04-11 15:05:26
发表评论】【查看原文】【C/C++讨论区】【关闭

[size=18][color=darkblue]const传奇    [/color][/size]
原文来自www.codeproject.com
作者:Rahul Singh

译者声明:有些地方按原文解释不通,译者根据自己的理解作了适当修改。如有不妥之处,请告知coolgrass@sina.com或参考原文。

 

简介

当我自己写程序需要用到const的时候,或者是读别人的代码碰到const的时候,我常常会停下来想一会儿。许多程序员从来不用const,理由是即使没用const他们也这么过来了。本文仅对const的用法稍作探讨,希望能够对提高软件的源代码质量有所帮助。

 

常变量

变量用const修饰,其值不得被改变。任何改变此变量的代码都会产生编译错误。Const加在数据类型前后均可。

例如:

void main(void)


{

    const int i = 10;    //i,j都用作常变量

    int const j = 20;

    i = 15;            //错误,常变量不能改变

    j = 25;            //错误,常变量不能改变

}

 

常指针

Const跟指针一起使用的时候有两种方法。

 

const可用来限制指针不可变。也就是说指针指向的内存地址不可变,但可以随意改变该地址指向的内存的内容。

void main(void)


{

    char* const str = "Hello, World";    //常指针,指向字符串

    *str = 'M';            //可以改变字符串内容

    str = "Bye, World";        //错误,如能改变常指针指向的内存地址

}


 

const也可用来限制指针指向的内存不可变,但指针指向的内存地址可变。

void main(void)


{

    const char* str = "Hello, World";    //指针,指向字符串常量

    *str = 'M';        //错误,不能改变字符串内容

    str = "Bye, World";    //修改指针使其指向另一个字符串

    *str = 'M';        //错误,仍不能改变字符串内容

}


看完上面的两个例子,是不是糊涂了?告诉你一个诀窍,在第一个例子中,const用来修饰指针str,str不可变(也就是指向字符的常指针);第二个例子中,const用来修饰char*,字符串char*不可变(也就是指向字符串常量的指针)。

 

这两种方式可以组合起来使用,使指针和内存内容都不可变。

void main(void)


{

    const char* const str = "Hello, World";        //指向字符串常量的常指针

    *str = 'M';            //错误,不能改变字符串内容

    str = "Bye, World";        //错误,不能改变指针指向的地址

}


 

Const和引用

引用实际上就是变量的别名,这里有几条规则:

声明变量时必须初始化

一经初始化,引用不能在指向其它变量。

任何对引用的改变都将改变原变量。 

引用和变量本身指向同一内存地址。

 

下面的例子演示了以上的规则:

void main(void)


{

    int i = 10;                    //i和j是int型变量

    int j = 20;        

    int &r = i;                    //r 是变量i的引用

    int &                        //错误,声明引用时必须初始化

    i = 15;                        //i 和 r 都等于15

    i++;                        //i 和 r都等于16

    r = 18;                        //i 和r 都等于18

    printf("Address of i=%u, Address of r=%u",&i,&r);    //内存地址相同

    r = j;                        //i 和 r都等于20,但r不是j的引用

    r++;                        //i 和 r 都等于21, j 仍等于20

}

 

用const修饰引用,使应用不可修改,但这并不耽误引用反映任何对变量的修改。Const加在数据类型前后均可。

例如:

void main(void)


{

    int i = 10;

    int j = 100;

    const int &r = i;

    int const &s = j;

    r = 20;          //错,不能改变内容

    s = 50;          //错,不能改变内容

    i = 15;          // i和r 都等于15

    j = 25;          // j和s 都等于25

}


 

Const和成员函数

声明成员函数时,末尾加const修饰,表示在成员函数内不得改变该对象的任何数据。这种模式常被用来表示对象数据只读的访问模式。例如:

class MyClass


{

    char *str ="Hello, World";

    MyClass()

    {

        //void constructor

    }

    

    ~MyClass()

    {

        //destructor

    }

 

    char ValueAt(int pos) const    //const method is an accessor method

    {

        if(pos >;= 12)

               return 0;

              *str = 'M';          //错误,不得修改该对象

        return str[pos];          //return the value at position pos

    }

}


 

Const和重载

重载函数的时候也可以使用const,考虑下面的代码:

class MyClass


{

    char *str ="Hello, World";

    MyClass()

    {

        //void constructor

    }

    

    ~MyClass()

    {

        //destructor

    }

 

    char ValueAt(int pos) const    //const method is an accessor method

    {

        if(pos >;= 12)

               return 0;

        return str[pos];    //return the value at position pos

    }

    

    char& ValueAt(int pos)        //通过返回引用设置内存内容

    {

        if(pos >;= 12)

               return NULL;

        return str[pos];

    }

}


 

在上面的例子中,ValueAt是被重载的。Const实际上是函数参数的一部分,在第一个成员函数中它限制这个函数不能改变对象的数据,而第二个则没有。这个例子只是用来说明const可以用来重载函数,没有什么实用意义。

 

实际上我们需要一个新版本的GetValue。如果GetValue被用在operator=的右边,它就会充当一个变量;如果GetValue被用作一元操作符,那么返回的引用可以被修改。这种用法常用来重载操作符。String类的operator[]是个很好的例子。(这一段译得很烂,原文如下:In reality due to the beauty of references just the second definition of GetValue is actually required. If the GetValue method is used on the the right side of an = operator then it will act as an accessor, while if it is used as an l-value (left hand side value) then the returned reference will be modified and the method will be used as setter. This is frequently done when overloading operators. The [] operator in String classes is a good example.)

 

class MyClass


{

    char *str ="Hello, World";

    MyClass()

    {

        //void constructor

    }

    

    ~MyClass()

    {

        //destructor

    }

 

    char& operator[](int pos)        //通过返回引用可用来更改内存内容

    {

        if(pos >;= 12)

               return NULL;

        return str[pos];

    }

}

 

void main(void)

{

    MyClass m;

    char ch = m[0];        //ch 等于 'H'

    m[0] = 'M';        //m的成员str变成:Mello, World

}


 

Const的担心

C/C++中,数据传递给函数的方式默认的是值传递,也就是说当参数传递给函数时会产生一个该参数的拷贝,这样该函数内任何对该参数的改变都不会扩展到此函数以外。每次调用该函数都会产生一个拷贝,效率不高,尤其是函数调用的次数很高的时候。

例如:

 

class MyClass


{

public:

    int x;    

    char ValueAt(int pos) const    //const method is an accessor method

    {

        if(pos >;= 12)

               return 0;

        return str[pos];    //return the value at position pos

    }

    MyClass()

    {

        //void constructor

    }

    ~MyClass()

    {

        //destructor

    }

    MyFunc(int y)    //值传递

    {

        y = 20;

        x = y;    //x 和 y 都等于 20.

    }

}

 

void main(void)

{

    MyClass m;

    int z = 10;

    m.MyFunc(z);

    printf("z=%d, MyClass.x=%d",z,m.x);    //z 不变, x 等于20.

}


 

通过上面的例子可以看出,z没有发生变化,因为MyFunc()操作的是z的拷贝。为了提高效率,我们可以在传递参数的时候,不采用值传递的方式,而采用引用传递。这样传递给函数的是该参数的引用,而不再是该参数的拷贝。然而问题是如果在函数内部改变了参数,这种改变会扩展到函数的外部,有可能会导致错误。在参数前加const修饰保证该参数在函数内部不会被改变。

class MyClass


{

public:

    int x;

    MyClass()

    {

        //void constructor

    }

    ~MyClass()

    {

        //destructor

    }

    int MyFunc(const int& y)    //引用传递, 没有任何拷贝

    {

        y =20;    //错误,不能修改常变量

        x = y    

    }

}

 

void main(void)

{

    MyClass m;

    int z = 10;

    m.MyFunc(z);

    printf("z=%d, MyClass.x=%d",z,m.x);    //z不变, x等于10.

}


 

如此,const通过这种简单安全机制使你写不出那种说不定是什么时候就会掉过头来咬你一口的代码。你应该尽可能的使用const引用,通过声明你的函数参数为常变量(任何可能的地方)或者定义那种const method,你就可以非常有效确立这样一种概念:本成员函数不会改变任何函数参数,或者不会改变任何该对象的数据。别的程序员在使用你提供的成员函数的时候,不会担心他们的数据被改得一塌糊涂。



 unicorns 回复于:2003-05-26 14:04:28

up


 robinliu76 回复于:2003-05-27 20:21:26

真的不错啊,顶一下!


 xdy 回复于:2003-05-27 22:11:58

写得很好
不错
希望继续写出更好的东西


 fightxxx 回复于:2003-07-11 22:27:17

问个问题啊:有类Point和类Rect,其中Rect::public Point
则:
Point point;
point.out();
Rect rect;
rect.out();
const Point *p=&
这个const的用法是属于哪一种啊?属于常指针吗??


 无双 回复于:2003-07-11 22:52:29

自己写一下代码试看啊

常指针就是指针地址不可变
常量 指针指向的值不可变
如果是一个类 那么不能调用没有const的方法


 lenovo 回复于:2003-07-11 23:51:39

不错!


 fightxxx 回复于:2003-07-12 09:33:16

调试了一下:
是个常量指针并且const rect *p等于rect const *p;指针p不能调用没有const的方法


 fieryfox 回复于:2003-07-12 09:33:37

这个翻译实在不怎么样,怎么还往上顶?l-value都被译成了“一元操作符”,译者的C水平恐怕只是入门时只抬起脚还没落下。当然译者也说了“译的很烂”,既然这样,真是不该发出来害人。
不针对人,只针对事。另外建议大家都去看原文,别看这个玩意了。const的概念里最重要的lvalue都搞不清楚,别的就不说了。


 fightxxx 回复于:2003-07-12 09:35:37

原文在哪能找到啊??


 fieryfox 回复于:2003-07-12 09:39:57

http://www.codeproject.com/useritems/StoryConst.asp


 胡青牛 回复于:2003-07-12 15:16:28

引用:原帖由 "HopeCao" 发表:

 

void main(void) 



    char* const str = "Hello, World";    //常指针,指向字符串 

    *str = 'M';            //可以改变字符串内容 

    str = "Bye, World";        //错误,如能改变常指针指向的内存地址 

}

  


str 是指针, 怎么可以把字符串赋给指针??!!!
即使去掉 const 也仍是错的啊!:(  

大家看看是不是


 胡青牛 回复于:2003-07-12 15:23:55

老大你转帖也要先看看啊~~

我们都是新手上路啊 :(


 nmgzw 回复于:2003-07-12 15:29:11

字符串在意义上好像等价于指针!


 胡青牛 回复于:2003-07-12 15:40:04

引用:原帖由 "nmgzw"]字符串在意义上好像等价于指针!
     


不要瞎说啊 , 你能举个例子吗???


你放屁把, 哈哈哈~  :)


 无双 回复于:2003-07-12 15:44:39

引用:原帖由 "胡青牛" 发表:
 


str 是指针, 怎么可以把字符串赋给指针??!!!
即使去掉 const 也仍是错的啊!:(  

大家看看是不是

     

因为字符串也保存在进程地址中
所以可以直接让指针指向字符串


 胡青牛 回复于:2003-07-12 15:52:19

那你认为这代码对吗?? 我只是去掉了const

void main(void) 


char* str = "Hello, World"; //常指针,指向字符串 

*str = 'M'; //可以改变字符串内容 

str = "Bye, World"; //错误,如能改变常指针指向的内存地址 

}


 lenovo 回复于:2003-07-12 16:30:43

引用:原帖由 "胡青牛" 发表:
那你认为这代码对吗?? 我只是去掉了const

void main(void) 


char* str = "Hello, World"; //常指针,指向字符串 

*str = 'M'; //可以改变字符串内容 

str = "Bye, World"; //错误,如能改变常指针..........

     
你的代码是对的,但是那些注释不对。


 胡青牛 回复于:2003-07-12 16:45:44

别管注释, 我只是改了一下原帖的。

我说代码是错的!!!!不信? 运行看看~
str = "Bye, World";  //错!!!!


 lenovo 回复于:2003-07-12 17:29:52

引用:原帖由 "胡青牛" 发表:
别管注释, 我只是改了一下原帖的。

我说代码是错的!!!!不信? 运行看看~
str = "Bye, World";  //错!!!!

     
没错的,我在vc6下编译连接都没问题。


 胡青牛 回复于:2003-07-12 17:34:59

编译当然没问题, 运行的了啊?

它明明是错的, 你怎么说它是对的~


 lenovo 回复于:2003-07-12 17:44:52

引用:原帖由 "胡青牛" 发表:
编译当然没问题, 运行的了啊?

它明明是错的, 你怎么说它是对的~

  
我在vc下运行了一下,有错误。   
在turbo c下是可以编译通过并成功运行的,
对于char * str = "hello world!",
我是这样理解的,vc编译器把它当作是常量字符串,
所以不能改变它,但是改变它指向的地址是可以的。
这和编译器有关,vc更加严格。下面的代码是正确的:
#include <stdio.h>;

void main(void) 


char* str = "Hello, World"; //?amp;?amp;?amp;&&?amp;熄啊林·&&& 
printf("%s",str);

//*str = 'M'; //&梢?amp;摹?amp;×帧?amp;&&内容 
//printf("%s",str);

str = "Bye, World"; //&í唯?amp;&?amp;能&摹?amp;?amp;?amp;?amp;?amp;熄?amp;哪?amp;&&刂· 
printf("%s",str);

}



 wingc 回复于:2003-07-13 01:27:03

原文是从csdn转的吧,有很多错误的,我在那边回帖说了一下,保括上面各位在讨论的什么指针指字符串的问题,确实如lenovo说得那样

大家看这个就知道了
http://www.csdn.net/develop/Read_Article.asp?Id=18513

我觉得那个什么翻译者很不负责,原文的例子根本就不是这样的


 fightxxx 回复于:2003-07-13 17:06:12

#include <stdio.h>;
#include <iostream.h>;

可以的。

void main()
{
char *p;
p="dfdf";

printf(":%s",p);
}


 胡青牛 回复于:2003-07-13 19:09:39

我明白一切拉~~~~~~哈哈哈

不告诉你们 ;)

楼上的代码是没错, 但讲的不是一个意思。

原帖的那个例子是错上加错~~`  

本来想把HOPECAO骂个狗血淋头, 现在只想感激他了。。。哇哈哈 ;)

精华果然是精华!


 lenovo 回复于:2003-07-13 21:08:39

引用:原帖由 "胡青牛" 发表:
我明白一切拉~~~~~~哈哈哈

不告诉你们 ;)

楼上的代码是没错, 但讲的不是一个意思。

原帖的那个例子是错上加错~~`  

本来想把HOPECAO骂个狗血淋头, 现在只想感激他了。。。哇哈哈 ;)

精华果然是..........

     
玩的还挺深奥哦!


 _maple_leaf 回复于:2004-04-11 15:00:44

#include<iostream.h>;
void main()
{
int m=0,n=2;
    const int *p=&amp; //常量指针
    p=&amp;
cout<<"&m"<<&m<<' '<<"n="<<&n<<endl;
cout<<"p="<<p<<endl;

    int* const a=&amp;//指针常量
    //a=&amp;//错误,不允许改变指针常量的指向对象 
cout<<"&m"<<&m<<' '<<"n="<<&n<<endl;
    cout<<"a="<<a<<endl;



  :arrow: :idea:


 _maple_leaf 回复于:2004-04-11 15:02:20

#include<iostream.h>;
void main()
{
int m=0,n=2;
    const int *p=&amp; //常量指针
    p=&amp;
cout<<"&m"<<&m<<' '<<"n="<<&n<<endl;
cout<<"p="<<p<<endl;

    int* const a=&amp;//指针常量
    [color=red]a=&amp;//错误,不允许改变指针常量的指向对象 [/color]
cout<<"&m"<<&m<<' '<<"n="<<&n<<endl;
    cout<<"a="<<a<<endl;
}

 :idea:


 _maple_leaf 回复于:2004-04-11 15:03:19

#include<iostream.h>;
void main()
{
int m=0,n=2;
    const int *p=&amp; //常量指针
    p=&amp;
cout<<"&m"<<&m<<' '<<"n="<<&n<<endl;
cout<<"p="<<p<<endl;

    int* const a=&amp;//指针常量
    [color=red]a=&amp;//错误,不允许改变指针常量的指向对象 [/color]
cout<<"&m"<<&m<<' '<<"n="<<&n<<endl;
    cout<<"a="<<a<<endl;
}


 _maple_leaf 回复于:2004-04-11 15:05:26

#include<iostream.h>;
void main()
{
int m=0,n=2;
    const int *p=&amp; //常量指针
    p=&amp;
cout<<"&m"<<&m<<' '<<"n="<<&n<<endl;
cout<<"p="<<p<<endl;

    int* const a=&amp;//指针常量
    [color=red]a=&amp;//错误,不允许改变指针常量的指向对象 [/color]
cout<<"&m"<<&m<<' '<<"n="<<&n<<endl;
    cout<<"a="<<a<<endl;
}

 :idea: 
[color=green]对不起,一直从VC++中复制的,看不到变量名称[/color]

 发表:
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值