基本语言细节--浅析const用法---基于VC++2012环境验证

                   基本语言细节--浅析const用法---基于VC++2012环境验证

1.什么是const?

     常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。

     const int month=12;

    month=7;  //error C3892: 'month' : you cannot assign to a variable that is const    

2.为什么引入const?

     const 推出的初始目的,正是为了取代预处理指令define,消除它的缺点,同时继承它的优点。

   (1)宏的优点:

     用宏来取代常数,好处是:1)让代码更简洁明了。 2)方便代码维护

    对宏的处理,在编译过程中称为“预处理”。也就是说在正式编译前,编译器必须先将代码出现的宏,用其相应的宏值替换。在代码中使用宏表达常数,归根结底还是使用了立即数,并没有明确指定这个量的类型。这容易带来一些安全性的问题,所以C++使用另一更稳妥的方法来代替宏的这一功能。

  (2)二者的区别:

     1.类型和安全检查不同

#define宏没有类型,而const修饰的只读变量具有特定的类型;编译器可以对const修饰的常量进行类型安全检查,而define定义的知识简单的替换,这种宏常量,也常导致边际效应。

       2.编译器处理方式不同:

#define宏是在预编译阶段进行替换,而const修饰的只读变量是在编译的时候确定其值;

       3.存储方式不同:

#define定义的宏常量在内存中有若干个拷贝,而const定义的只读变量在程序运行过程中只有一份拷贝,故而节省了存储的空间;define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。const常量会在内存中分配(可以是堆中也可以是栈中)。分配空间与否,应视情况而定,判断标准就是编译器是否有足够的信息!!若是知道了所有的使用,则可以不分配存储;但是,若是没有,则需要。常量数组,需要分配空间,因为编译器无法知道表达式里使用的是数组的哪些元素!(参见C++程序设计语言P86)

       4.是否可调式:

有些集成的调试工具可以对const常量调试而不能对宏常量进行调试。

       5.效率不同:

编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。  

    (扩展知识:1.预处理:ISO C和ISO C++都规定程序由源代码被翻译分为若干有序的阶段(phase) ,通常前几个阶段由预处理器实现。预处理中会展开以#起始的行,试图解释为预处理指令(preprocessing directive) ,其中ISO C/C++要求支持的包括#if/#ifdef/#ifndef/#else/#elif/#endif(条件编译)、#define(宏定义)、#include(源文件包含)、#line(行控制)、#error(错误指令)、#pragma(和实现相关的杂注)以及单独的#(空指令)[1-2]。预处理指令一般被用来使源代码在不同的执行环境中被方便的修改或者编译。宏定义又称为宏代换、宏替换,简称“宏”。格式:#define 标识符 字符串。2.边际效应:在使用define定义的常量的过程中,由于大量的简单的替换所带来的,程序臃肿(因为这种工作方式就是以空间来换取时间),从而减少了由其带来的便利性。(个人理解))

3.const作用:

(1)可以定义const 常量:

const int Month=12;

(2)可以进行安全类型的检查:

         void DisplayMonth(const int month)
        {
       cout<<month;
        }

在main函数调用DisplayMonth(“love”)时,便有error C2664: 'DisplayMonth' : cannot convert parameter 1 from 'const char [5]' to 'const int'
1>          There is no context in which this conversion is possible

(3)保护被修饰的内容:

如在上述函数内加入“month++;”,便有 error C3892: 'month' : you cannot assign to a variable that is const

(4)使得代码更加容易维护:

如宏定义一样,减少了重复性。满足了在《程序员修炼之道--从小工到专家》中所提到的DRY(Don't repeat yourself)原则;

(5)为函数重载提供了参考:

如:我们可以在Point类(附录中查看)中添加public 函数:double GetX(void) ; double GetX(void) const;

double Point::GetX(void) 
{
return m_x+2;
}
double Point::GetX(void) const
{
return m_x+100;
}

在main函数中写出如下代码:Point xx;
const Point cc=xx;
cout<<xx.GetX()<<endl;
cout<<cc.GetX()<<endl;

输出结果为:2  100     

(6)节省空间,避免不必要的内存分配:

如2中所述。

(7)提高了效率。


4.const几个典型的应用:

1.const修饰指针:

         (1)const Point *ppoint;     //ppoint 可变,其所指内容不可变。(可以直接定义而不初始化,但是会出现 warning C4101: 'ppoint' : unreferenced local variable)

        (2) Point const  *ppoint;    //同上,所出现的警告也如上;

         (3) Point   * const ppoint;  //ppoint不可变,其所指内容可以改。(必须在定义时初始化,error C2734: 'ppoint' : const object must be initialized if not extern)

         (4)const Point * const ppoint //ppoint不可变,其所指内容也不可改变。(必须在定义时初始化,error C2734: 'ppoint' : const object must be initialized if not extern

    此节可以参照《Effective C++》Scott Meyers著 中的精彩描述。

2.const修饰函数参数:

       (1)传递过来的参数在函数内不可以改变。(可以提高代码的可读性,参照《Code Complete》"为人编程,而不是机器")

 void DisplayMonth(const int month);//意思是说,month在函数体内不会被改变。

   (2)当然,若是指针,则视const位置而定:

void DisplayMonth(const Point *ppoint); //ppoint所指向的对象不可变,

void DisplayMonth( Point * const ppoint);//ppoint不可变

void DisplayMonth(const Point * const ppoint);//均不可变(其应用可以参见《深度探索C++对象模型》中的function语意学)

    (3)传递引用与指针类似;(指针与引用的不同参见后期文章吧!大笑

3.类中的使用

    (1)const修饰成员变量:

表示成员常量,不能被修改,同时它只能在初始化列表中赋值。(在VC中常量成员可以被常成员函数或者非常成员函数访问,但是二者均不可修改);

 如在Point添加私有常量成员const float num;但是在VC自动添加在构造函数的初始化列表中的num初始化删除,重新编译,结果是 error C2758: 'Point::num' : must be initialized in constructor base/member initializer list
1>          d:\mysoftware\test\test\point.h(19) : see declaration of 'Point::num'

    (2)const修饰类的成员函数:

A. const修饰类的成员函数,则该成员函数不能修改类中任何非const成员函数。一般写在函数的最后来修饰,并且声明处与定义处,都应该存在。否则会出错!

B.const类对象/引用/ 指针,只能调用类的const成员函数。因为任何非const成员函数会有修改成员变量的企图。

原理:在《深度探索C++对象中》中讲解function语意学,成员函数都会被重写成一个外部函数,比如,

一个const的非静态的成员函数,会被编译器改写成extern void GetX(const Point * const this);若是调用了非常成员函数,则会出现错误: error C2662: 'Point::SetX' : cannot convert 'this' pointer from 'const Point' to 'Point &'
1>          Conversion loses qualifiers

5.const类型转化为非const类型:

采用const_cast 进行转换。


6.const在C++与C中的区别:

     const int month;

 此句编译会出错:error C2734: 'month' : const object must be initialized if not extern。

原因:C++中const默认是内部连接,如果想在C++中达到以上的效果,必须要用extern关键字. C++中,const默认使用内部连接.而C中使用外部连接。故而在C语言中: const int size; 这个语句是正确的,因为它被C编译器看作一个声明,指明在别的地方分配存储空间.

(扩展知识:1.内连接编译器只对正被编译的文件创建存储空间,别的文件可以使用相同的表示符或全局变量.C/C++中内连接使用static关键字指定。2.连接:所有被编译过的文件创建一片单独存储空间.一旦空间被创建,连接器必须解决对这片存储空间的引用.全局变量和函数使用外部连接.通过extern关键字声明,可以从其他文件访问相应的变量和函数. 

7.const在使用中的建议:

要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;

8.参考书目:

    1.网络诸多大侠;

    2.《Effective C++》

    3.《深度探索C++对象模型》

    4.《代码大全》

    5.《程序员修炼之道》

                            


           wolangjushi 于HUST 2013.11.13     



附录:Point类:


 class Point
{
public:
Point(void);
~Point(void);
public:
void SetX(double x);
void SetY(double y);
void SetZ(double z);
double GetX(void) ;
double GetX(void) const;
double GetY(void) const;
double GetZ(void) const;
private:
double m_x;
double m_y;
double m_z;
const float num;
};

Point::Point(void)
: m_x(0)
, m_y(0)
, m_z(0)
,num(5)


{


}




Point::~Point(void)
{
}




void Point::SetX(double x)
{
//m_x=x;


}




void Point::SetY(double y)
{
m_y=y;
}




void Point::SetZ(double z)
{
m_z=z;
}


double Point::GetX(void) 
{
return m_x+2+num;
}




double Point::GetX(void) const
{
return m_x+100;
}




double Point::GetY(void) const
{


return m_y+num;
}




double Point::GetZ(void) const
{
return m_z;
}






   


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值