Chinaunix首页 | 论坛 | 博客
  • 博客访问: 158642
  • 博文数量: 24
  • 博客积分: 3133
  • 博客等级: 中校
  • 技术积分: 206
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-24 11:08
文章分类

全部博文(24)

文章存档

2010年(4)

2009年(20)

分类: C/C++

2009-05-16 22:21:44

指定变量的值不能被修,const作为一个类型限定词,和int有相同的地位.
如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。

Thinking again in C++(一)常量性原理 cphj(原作)

1.不能将const修饰的任何对象、引用和指针作为赋值表达式的左值。
    const int cx = 100;
    const int & rcx = cx;
    const int * pcx = &cx;
    cx = 200;                 //error
    rcx = 200;                 //error
    *pcx = 200;             //error

2.const类型的对象不能直接被non-const类型的别名所引用。
    (1)不能将const类型的对象传递给non-const类型的引用。
        const int cx = 100;
        int & rx = cx;         //error
    (2)不能将const类型的实参传递给形参为non-const类型引用的函数。
        void f(int a)
        {
        }
        void g(int & ra)
        {
        }
        const int cx = 100;
        f(cx);                  //ok
        g(cx);                 //error
    (3)不能将const类型的对象作为non-const类型引用的函数返回值。
        int & f(const int & rca)
        {
            return rca;     //error
        }
        int x = 100;
        f(x);

3.可以使用const类型别名引用non-const对象。此时通过const引用不能修改对象,但对象可以通过non-const引用被修改。
    int x = 100;
    int & rx = x;
    const int & rcx = x;     //ok
    x = 200;
    rx = 200;
    rcx = 200;                 //error

4.指针的属性有两个:指针的类型和指针本身的常量性。其中,指向const对象与指向non-const对象,是不同的指针类型。
    int x = 100;
    const int * pcx = &x;     //[1]
    int * px = &x;              //[2]
    int y = 100;
    int * const cpy = &y;     //[3]
    int * py = &y;              //[4]
    [1][2]两个指针的类型不同;[3][4]两个指针的常量性不同。
    对象与指向对象的指针的规则类似于对象与引用。即,
        const类型的对象不能直接被non-const类型的指针所指示(同2);
        可以使用const类型的指针指向non-const对象(同3)。

5.可以将相同类型(包括常量性)的const指针值赋给non-const指针。
    int x = 100;
    int * px;
    const int * pcx = &x;
    px = pcx;                 //error
    int * const cpx = &x;
    px = cpx;                 //ok

6.若函数的返回值为内建类型或是指针,则该返回值自动成为const性质。但自定义类型则为non-const性质。
    int f()                 //相当于返回const int
    {
        return 100;
    }
    int * g(int & ra)         //相当于返回int * const
    {
        return &ra;
    }
    class CTest{
        int n;
        public:
            CTest(int n){this->n = n;}
    };
    CTest h()                 //返回的就是CTest
    {
        return CTest(200);
    }

    f() = 200;                 //error

    int x = 100;
    int y = 200;
    int * px = &x;
    g(y) = px;                 //error
    *g(y) = x;                 //ok,从这点可以看出g()返回的不是const int *

    CTest t(100);
    h() = t;                  //ok,但却是完全错误的、危险的做法
                               //所以h()的正确写法是返回const CTest


1.const常量,如const int max = 100; 
    优点:const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误(边际效应)
2.const 修饰类的数据成员。如:
    class A{
        const int size;
        …
    }   
    const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const 数据成员的值是什么。如
    class A{
    const int size = 100;    //错误
    int array[size];         //错误,未知的size
    }
    const数据成员的初始化只能在类的构造函数的初始化表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现。如
    class A{…
    enum {size1=100, size2 = 200 };
    int array1[size1];
    int array2[size2];
    }
    枚举常量不会占用对象的存储空间,他们在编译时被全部求值。但是枚举常量的隐含数据类型是整数,其最大值有限,且不能表示浮点数。 
3.const修饰指针的情况,见下式:
    int b = 500;
    const int *a = &           [1]
    int const *a = &           [2]
    int* const a = &           [3]
    const int* const a = &     [4]

如果你能区分出上述四种情况,那么,恭喜你,你已经迈出了可喜的一步。不知道,也没关系,我们可以参考《Effective c++》Item21上的做法,如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。因此,[1]和[2]的情况相同,都是指针所指向的内容为常量(const放在变量声明符的位置无关),这种情况下不允许对内容进行更改操作,如不能*a = 3 ;[3]为指针本身是常量,而指针所指向的内容不是常量,这种情况下不能对指针本身进行更改操作,如a++是错误的;[4]为指针本身和指向的内容均为常量。
4.const的初始化
    先看一下const变量初始化的情况
    1) 非指针const常量初始化的情况:A b;
        const A a = b;
    2) 指针const常量初始化的情况:
        A *d = new A();
        const A *c = d;
      或者:
        const A *c = new A();
    3)引用const常量初始化的情况:
        A f;
        const A& e = f;      // 这样作e只能访问声明为const的函数,而不能访问一般的成员函数;

    [思考1]: 以下的这种赋值方法正确吗?
        const A *c=new A();
        A *e = c;
    [思考2]: 以下的这种赋值方法正确吗?
        A* const c = new A();
        A* b = c;

5.另外const 的一些强大的功能在于它在函数声明中的应用。在一个函数声明中,const 可以修饰函数的返回值,或某个参数;对于成员函数,还可以修饰是整个函数。有如下几种情况,以下会逐渐的说明用法:       
    A& operator=(const A& a);
    void fun0(const A* a );
    void fun1( ) const;     //fun1( )为类成员函数
    const A fun2( );

    1)修饰参数的const,如
        void fun0(const A* a );
        void fun1(const A& a);
    调用函数的时候,用相应的变量初始化const常量,则在函数体中,按照const所修饰的部分进行常量化,如形参为const A* a,则不能对传递进来的指针的内容进行改变,保护了原指针所指向的内容;如形参为       
    const A& a,则不能对传递进来的引用对象进行改变,保护了原对象的属性。
    [注意]:参数const通常用于参数为指针或引用的情况,且只能修饰输入参数;若输入参数采用“值传递”方式,由于函数将自动产生临时变量用于复制该参数,该参数本就不需要保护,所以不用const修饰。


const主要是为了程序的健壮型,减少程序出错.
最基本的用法:
    const int a=100;     //a的内容不变,a只能是100也就是声明一个int类型的常量(#define a =100)
    int const b=100;     //和上面作用一样

const指针和引用一般用在函数的参数中
    int* m = &a;         //出错,常量只能用常指针
    int c = 1;
    const int *pc = &c;    //常指针可指向变量

    const int* pa = &a; //指针指向的内容为常量(就是b的值不变)
    int const *a = &b;     //指针指向的内容为常量(就是b的值不变)*p=3//error
    int* const a = &b;     //指针为常量,不能更改指针了如 a++但可以改值*p=3;
从这可以看出const放在*左侧修饰的是指针的内容,const放在*右侧修饰的是指针
本身.

const引用的用法和指针一样
    int const & a = b;     //和指针一样
    const int& a = b;     //和指针一样
    但没有 int& const a = b 的用法因为引用不能做移位运算,但只是出个warning

const int* const a = &b; //综合应用,一般用来传递多维的数组
    类如:char* init[] = {"Paris","in the","Spring"};
void fun(const int* const a){}
    fun(init)            //保护参数不被修改

int A(int)const;         //是常函数,只能用在类中,调用它的对象不能改改变成员值
const int A();             //返回的是常量,所以必须这么调用 cosnt int a=A();
int A(const int);         //参数不能改值,可用在任意函数
int A(const int*);
....
int height() const;        //常函数只能由常函数调用
int max(int,int) const;
int Max = max(height(),height());

const int* pHeap = new int;
delete pHeap;
p = NULL;//出错
我的解决办法是强制类型转换
const int* pHeap = new int(1);
delete (int*)pHeap;
pHeap = NULL;

一、const 和引用联合使用的时候要注意

    const int a = 1;
    const int& ref1 = a;
    const int& ref2 = 1;

ref1 和 ref2 都是正确的,但是他们引用的内容和一般的引用不同
对 const int& ref1 = a; 而言,其实这个 ref1 已经和 a 没有任何关系了ref1 实际上是对一个临时量的引用。
同理 const int& ref2 = 1; 也是对一个临时量做的引用。当引用临时量是 C++ 的隐式类型转换可以起作用。
临时量的生存期和引用量的生存期相同。

二、强传const对象可能导致无定义行为

    对于优化做的比较好的编译器,代码
        const int i = 1;
    当后面用到变量 i 的时候,编译器会优化掉对 i 的存取,而直接使用立即数 1

    const int i = 1;
   
    *(const_cast(&i)) = 2;
    cout << *(int*)&i << endl;
    cout << i << endl;

    所以,对 const 对象做 const_cast 可能导致无定义行为
       
    这个就是我在调错时发现的
        int height() const;    //常函数只能由常函数调用
        int max(int,int) const;
        int Max = max(height(),height());


阅读(1607) | 评论(1) | 转发(0) |
0

上一篇:转义字符

下一篇:数据类型

给主人留下些什么吧!~~

chinaunix网友2010-01-20 08:48:11

很好 很好 很有用 总结的蛮全的