zoukankan      html  css  js  c++  java
  • c/c++中的const关键【转载】

    const,c\c++中非常常用的关键字。
    首先我们看看const有那些修饰方法。
    最前面我们来看一个问题。

    1. char *p = "123"; 

    可能有的人不知道,这里的这个"123"是存储在常量区的,不容许修改的。
    也就是说它是个const类型的。
    标准的写法应该是

    1. char const *p = "123"; 

    至于为什么编译器容许上面的那个不标准的写法,可能是因为兼容性的问题吧。

    1. char p[4] = "123";//这个和上面的开始大不一样的啊。

    切入正题。

    1. const int s = 0; 

    上面的很容易理解。但带上指针有些同学可能就要昏了。

    1. const int *p = &s;//(1)
    2. int const *p = &s;//(2)
    3. int * const p = &s;//(3)
    4. const int * const p = &s;//(4)

    (1)和(2)的效果是一样的,意思是 p 是可变的,但是 p 指向的值是不可变的。
    (3)就刚好相反,p 是不可变的,只能指向一个地方,但是 p 指向的值是可变的。
    (4)p不可变,*p 也不可变。
    我们举个例子看看。

    1. int s = 0; 
    2. const int *p = &s; 
    3. p++;//可以
    4. p--;//可以
    5. *p = 4;//错误

    ------------------------------

    1. int s = 0; 
    2. int * const p = &s; 
    3. *p = 4;//可以
    4. p++;//错误

    ------------------------------
    有的时候我们会使用tpyedef。

    1. typedef char * pStr; 
    2. char str[4] = "123"; 
    3. const char *p1 = str;// p1可变,*p1不可变
    4. const pStr p2 = str;// 和上面的意思不一样哦,p2不可变,*p2可变
    5. p1++;//可以
    6. p2++;//错误

    -------------------------------
    有时候我们会使用括号

    1. (int *) const p;//p是不可变,*p可变
    2. const (char *) p;//p是不可变,*p可变

    -----------------------------------------------------------

    在c中 被const修饰的变量并不是正真意义上的常量,而是只读的变量,这和常量还有不少的差距的。
    也就是说在内存中开辟一个只读的空间存贮这个变量,如何保证这个空间只读是通过编译器实现的,
    当我们能骗过编译器的时候,我们就能就能修改const常量。
    在c代码中写:

    1. const int num = 9; 
    2. int ary[num]; 

    这段代码是不能通过编译的,如果真的想这么干只能通过宏来实现了。

    1. #define NUM = 9;
    2. int ary[NUM]; 

    大家都知道宏替换的一些弊端,比如:NUM没有类型,从此NUM这个名字再也没有别的用途。
    -------------------------------------------------------------

    而c++中的const就先进的多了。他是在编译的时候就已经确定了的值。

    1. const int num = 9; 
    2. int ary[num]; 

    同样的代码在C++中就一点问题都没有了。
    而且我们还可以通过强制转换去掉其const属性。

    1. const int a = 4; 
    2. const int *p = &a; 
    3. int *q; 
    4. q = const_cast<int *>( p ); 

    但是我们要明白的是c++中使用const_cast的目的不是要我们去修改一个const量(虽然我们可以用各种方法做到),
    而是让我们可以用一个 指向非const的指针(或引用) 去 指向一个const的变量,通常在函数调用时使用。
    下面我们也尝试一下修改const量。
    很简单:

    1. const int a = 4; 
    2. const int *p = &a; 
    3. int *q; 
    4. q = const_cast<int *>( p ); 
    5. std::cout << a << std::endl; 
    6. *q = 9; 
    7. std::cout << a<< std::endl; 

    OK了。
    有的同学试验了,可能出问题了。
    a打印出来都是4啊,没变啊。
    其实是变了。
    如果我们打印 *p,*q 发现其值是9。
    我们打印p,q,&a地址也是一样的。
    这就更奇怪了,同一个地址怎么可能有2个值。
    我来告诉大家为什么
    我们的编译器是很强大的,他会对代码进行一些优化,
    当我们定义变量a之后,再使用a的时候,没有从a的地址去取值,而是直接使用了4 。
    所以打印的还是4。
    有人会觉得我在耍他,不信我们使用下面的代码

    1. const volatile int a = 4;//尽管非常奇怪,你const了还易变。。
    2. const volatile int *p = &a; 
    3. int *q; 
    4. q = const_cast<int *>( p ); 
    5. std::cout << a << std::endl; 
    6. *q = 9; 
    7. std::cout << a<< std::endl; 

    此时下面打印的就是9了。
    所以啊,我们不要随便去改const量,常常会有一些出乎我们意料的结果。
    既然定义了const心里就想着他是常量了。

    ----------------------------------------------------------

    const还可以用来修饰方法,不过const就不是像static那样放在函数的前面了,
    因为放在前面的const已经有意义了,是指函数的返回值是const的。这样只能放在函数的最后了。
    例如:

    1. int get( ) const; 

    有什么作用呢。他的意思就是这个方法不能修改类成员变量的值。
    例如

    1. class human 
    2. private: 
    3. int m_gender; 
    4. int m_age; 
    5. public: 
    6.     human():m_gender(0),m_age(0) 
    7.     { 
    8.     } 
    9. int getAge1( ) const
    10.     { 
    11.         m_age = 20;//错误,试图在const方法中修改类成员变量
    12.          m_gender = 1;//错误,试图在const方法中修改类成员变量
    13. return m_age; 
    14.     } 
    15. int getAge2( ) 
    16.     { 
    17.         m_gender = 1;//正确
    18.          m_age = 20;//正确
    19. return m_age; 
    20.     } 
    21. }; 

    如果我们定义一个const的human对象。那么我们使用这个const的对象的方法时只能使用被const修饰的

    方法。

    1. const human a; 
    2. a.getAge1();//正确
    3. a.getAge2();//错误

    有的同学有疑问了,如果类中有的值是需要在const时改变的怎么办。
    C++此时又引入了一个很好的关键字mutable

    1. class human 
    2. private: 
    3. int m_gender; 
    4. mutable int m_age; 
    5. public: 
    6.     human():m_gender(0),m_age(0) 
    7.     { 
    8.     } 
    9. int getAge1( ) const
    10.     { 
    11.         m_age = 20;//正确。使用了mutable关键字
    12.          m_gender = 1;//错误,试图在const方法中修改类成员变量
    13. return _age; 
    14.     } 
    15. int getAge2( ) 
    16.     { 
    17.         m_gender = 1;//正确
    18.          m_age = 20;//正确
    19. return m_age; 
    20.     } 
    21. }; 

    下面谈一下一点的其他问题:
    见过别人写这样的代码:

    1. class string 
    2. private: 
    3. char *m_pStr; 
    4. public: 
    5.     string( const char *pstr = NULL ) 
    6.     { 
    7.         m_pStr = new char[20]; 
    8.         memset( m_pStr, 0, 20 ); 
    9.         strcpy( m_pStr, pstr, 19 ); 
    10.     } 
    11. char *getName() 
    12.     { 
    13. return m_pStr; 
    14.     }  
    15.     ~string() 
    16.     { 
    17. delete[] m_pStr; 
    18.     } 
    19. }; 

    这样的代码看似没有问题,也可以正常运行,但是存在巨大的隐患,会给不法分子可乘之机。
    char *getName()这个方法。这个方法将我们的private变量暴露给了别人。
    我们就可以通过调用这个接口获得这个地址修改其中的值。

    1. int main( void ) 
    2.     string str("cc"); 
    3. char *p; 
    4.     p = str.getName();  
    5.     std::cout << p << std::endl; 
    6.     p[0] = '4'; 
    7.     std::cout << p << std::endl; 
    8. return 0; 

    我们可以将其改写为

    1. const char *getName() const

    这样

    1. int main( void ) 
    2.     string str("cc"); 
    3. const char *p; 
    4.     p = str.getName();  
    5.     p[0] = '4'; //报错
    6.     std::cout << p << std::endl; 
    7. return 0; 

    不过我们最好改写为

    1. int getName( char *str ) const//使用时确保str空间是够的。
    2. strcpy( str, m_pStr ); 

    const还是十分重要的啊。
    终于写完了,:-)。一定要认真阅读啊。
    不足及错误欢迎指正。const还有其他有趣的一些用法也欢迎分享啊。^_^。

    不容易啊,给点掌声吧。

  • 相关阅读:
    工作流调度器azkaban
    日志采集框架Flume
    MAPREDUCE框架结构及核心运行机制
    Python爬虫开发系列之五》数据存储为TXT、JSON格式
    Python爬虫开发系列之二》请求库及解析库安装
    策略模式+简单工厂模式
    简单工厂模式
    单例模式
    5、计算高可用
    4、存储高可用
  • 原文地址:https://www.cnblogs.com/zhongbin/p/2914526.html
Copyright © 2011-2022 走看看