C++中const关键字的使用指南

        使用const关键字描述的变量或对象的值是不能够被改变的。const使用得当的代码会更易维护,可靠性更强,我们应该尽可能的使用const,这会使你的程序获益良多,但前提是你必须搞清楚原委。const的用法如下:

        a. const修饰普通变量

        常量的表述形式:const 类型说明符 变量名

        在声明常量时,必须进行初始化。如果是类中的常数据成员,那么只能通过使用成员初始化列表方式的构造函数来对其进行赋值,因为类的成员未被创建时编译器不知道const数据成员的值是多少。因此下面的代码是错误的:

class A{
   const int i = 8;  //错误
};
        我们要这样对其赋值:
class A{
   const int i;
   A(int x):i(x){};
};
        还要注意的是,对于常变量、常引用、常对象、常数组,const与类型说明符的位置是可以互换的。以下例子都是等价的:
class A;
const A q;
A const q;

int const i = 8;
const int i = 8;

        在程序中,最好使用const来代替#define。当你使用#define SUM 36 时,记号名称SUM也许从未被编译器看到。在编译器处理源代码前,它也许就被预处理器移走了,于是记号名称SUM就没有进入记号表中。当你运用SUM但遇到编译器错误信息时,你会为此感到不解,因为错误信息只提到36而不是SUM。如果SUM被定义在一个非你所写的头文件中,你会付出大量的精力来调试错误。对于此问题的解决之道是用const来代替#define:const int SUM = 36  作为一个语言常量,SUM肯定会被编译器看到,当然会进入到记号表内。

        b. const修饰指针

        如果使用const来修饰指针,那么根据const所处的位置不同,其效果也不同。例如:
int i = 5;
const int* p = &i;//常量指针
*p = 10;//错误

        const出现在“*”的左侧表示常量指针,即指针所指向的内容不可修改。

int a = 1;
int b = 2;
int* p1 = &a;
int* const p2 = &b; //指针常量
p2 = p1; //错误
        const出现在“*”的右侧表示指针常量,即不允许该指针指向其他的内容。
int i = 6;
const int* const p = &i;
        以上两种情况的混合体,表示指针本身和指针所值内容均不可改变。

        c. const修饰引用

        const也可以用来修饰引用,表示引用所引用的对象的值不能被改变。

int i = 1;
const int& u = i; //正确
int& const uu = i; //错误,不能这样写

      

        d. const修饰函数

        在函数签名的后面加上const表示该函数是常成员函数,常成员函数不可修改类的数据成员的值,也不能调用非const成员函数。对成员函数使用const有两点原因,一是使类的接口更容易被理解。对于开发人员来说,得知哪个函数可以修改对象的值而哪个函数不行是很重要的。二是能够让我们操作const对象。例如:
class test{
   int i;
   void set() const;
};
void test::set() const {
   i = 100;
}

         在上面的代码中,常函数set()试图去修改变量i的值,这是错误的,会导致编译时出现异常。

         当然,对于非常量的成员函数,我们可以根据需要读取或修改数据成员的值。但是这要依赖调用函数的对象是否是常量。通常,如果我们把一个类定义为常量,我们的本意是希望他的状态(数据成员)不会被改变。那么,如果一个常对象调用它的非常函数会产生什么后果呢?看下面的代码:

class Fred{
public:
void inspect() const;
void mutate();
};
void UserCode(Fred& changeable, const Fred& unChangeable)
{
changeable.inspect(); // 正确,非常对象可以调用常函数。
changeable.mutate(); // 正确,非常对象也允许修改调用非常成员函数来修改数据成员。
unChangeable.inspect(); // 正确,常对象只能调用常函数,因为不希望修改对象状态。
unChangeable.mutate(); // 错误!常对象的状态不能被修改,而非常函数存在修改对象状态的可能
}
        从上面的代码可以看出,由于常对象的状态不允许被修改,因此,通过常对象调用非常函数时将会产生语法错误。实际上,我们知道每个成员函数都有一个隐含的指向对象本身的this 指针。而常函数则包含一个this 的常量指针。如下:
void inspect(const Fred* this) const;
void mutate(Fred* this);
        也就是说对于常函数,我们不能通过this 指针去修改对象对应的内存块。但是,在上面我们已经知道,这仅仅是编译器的限制,我们仍然可以绕过编译器的限制,去改变对象的状态。看下面的代码:
class Fred{
public:
void inspect() const;
private:
int intValue;
};
void Fred::inspect() const
{
cout << "At the beginning. intValue = "<< intValue << endl;
// 这里,我们根据this 指针重新定义了一个指向同一块内存地址的指针。
// 通过这个新定义的指针,我们仍然可以修改对象的状态。
Fred* pFred = (Fred*)this;
pFred->intValue = 50;
cout << "Fred::inspect() called. intValue = "<< intValue <<
endl;
}
int main()
{
Fred fred;
fred.inspect();
return 0;
}
        上面的代码说明,只要我们愿意,我们还是可以通过常函数修改对象的状态。同理,对于常对象,我们也可以构造另外一个指向同一块内存的指针去修改它的状态。这里就不作过多描述了。另外,也有这样的情况,虽然我们可以绕过编译器的错误去修改类的数据成员。但是C++也允许我们在数据成员的定义前面加上mutable,以允许该成员可以在常函数中被修改。例如:
class Fred{
public:
void inspect() const;
private:
mutable int intValue;
};
void Fred::inspect() const
{
intValue = 100;
}
        但是,并不是所有的编译器都支持mutable 关键字。这个时候我们上面的方法就有用了。关于常函数,还有一个问题是重载。
#include <iostream>
#include <string>
using namespace std;
class Fred{
public:
void func() const;
void func();
};
void Fred::func() const
{
cout << "const function is called."<< endl;
}
void Fred::func()
{
cout << "non-const function is called."<< endl;
}
void UserCode(Fred& fred, const Fred& cFred)
{
cout << "fred is non-const object, and the result of fred.func()
is:" << endl;
fred.func();
cout << "cFred is const object, and the result of cFred.func()
is:" << endl;
cFred.func();
}
int main()
{
Fred fred;
UserCode(fred, fred);
return 0;
}
        输出结果为:
fred is non-const object, and the result of fred.func() is:
non-const function is called.
cFred is const object, and the result of cFred.func() is:
const function is called.
       从上面的输出结果,我们可以看出。当存在同名同参数和返回值的常函数和非常函数时,具体调用哪个函数是根据调用对象是常对像还是非常对象来决定的。常对象调用常量成员,非常对象调用非常量的成员。

        e. const修饰函数参数

        const修饰函数参数是其最广泛的用途之一,表示函数不能改变参数所指的值。

void fun1(const int Var); //传递过来的参数在函数内不可以改变(无意义,因为Var本身就是形参)
void fun2(const char* Var); //参数指针所指内容为常量不可变
void fun3(char* const Var); //参数指针本身为常量不可变(也无意义, 因为char* Var也是形参)

        f. const修饰函数返回值

       很多时候,我们的函数中会返回一个地址或者引用。调用这得到这个返回的地址或者引用后就可以修改所指向或者代表的对象。这个时候如果我们不希望这个函数的调用这修改这个返回的内容,就应该返回一个常量。
        

        总结:

        1. 要尽可能的使用const,这回为你的程序带来很大的益处。

        2. 在程序中,尽量使用const来代替#define。

        3. 当函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载。除了操作符重载,一般不要将返回值类型定义为某个对象的const引用。

        4. 在参数中使用const,要使用引用或指针,而不是一般的对象实例。

        5. const在函数中的3种用法(参数、返回值、函数)要很好地运用。

        6. C++中的const 正常情况下是看成编译期的常量,编译器并不为const 分配空间,只是在编译的时候将期值保存在名字表中,并在适当的时候折合在代码中。所以,以下代码:

using namespace std;
int main()
{
const int a = 1;
const int b = 2;
int array[ a + b ] = {0};
for (int i = 0; i < sizeof array / sizeof *array; i++)
{
cout << array << endl;
}
}
        可以通过编译,并且正常运行。但稍加修改后,放在C 编译器中便会出现错误:
int main()
{
int i;
const int a = 1;
const int b = 2;
int array[ a + b ] = {0};
for (i = 0; i < sizeof array / sizeof *array; i++)
{
printf("%d",array);
}
}
        错误消息:
c:\test1\te.c(8): error C2057: 应输入常数表达式
c:\test1\te.c(8): error C2466: 不能分配常数大小为 0 的数组
        出现这种情况的原因是:在C 中,const 是一个不能被改变的普通变量,既然是变量,就要占用存储空间,所以编译器不知道编译时的值,而且数组定义时的下标必须为常量.














  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值