首页 > 代码库 > c++中const关键字全面总结

c++中const关键字全面总结

一、const作用

1、const定义常量

注意:const只对它左边的东西起作用,唯一的例外就是const本身就是最左边的修饰符,那么它才会对右边的东西起作用。

(1)const修饰变量,以下两种定义形式在本质上是一样的。它的含义是:const修饰的类型为TYPE的变量value是不可变的。

 TYPE const ValueName = value; 
 const TYPE ValueName = value;


(2)将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义.

     extend const int ValueName = value;


2、const修饰指针


(1)指针本身是常量不可变
     (char*) const pContent; 
     const (char*) pContent; 


(2)指针所指向的内容是常量不可变
     const (char) *pContent; 
     (char) const *pContent; 

(3)两者都不可变
      const char* const pContent; 

(4)还有其中区别方法,沿着*号划一条线:
如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。


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

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


[思考题答案] 
1) 这种方法不正确,因为声明指针的目的是为了对其指向的内容进行改变,而声明的指针e指向的是一个常量,所以不正确; 
2) 这种方法正确,因为声明指针所指向的内容可变; 


 (5) 常量与引用

常量与引用的关系稍微简单一点。因为引用就是另一个变量的别名,它本身就是一个常量。也就是说不能再让一个引用成为另外一个变量的别那么他们只剩下代表的内存区域是否可变。即:

    // 正确:表示不能通过该引用去修改对应的内存的内容。

    const int& ri = i;

    // 错误!不能这样写。

    int& const rci = i;

    由此可见,如果我们不希望函数的调用者改变参数的值。最可靠的方法应该是使用引用



3、函数中使用const

(1)const修饰函数参数
a.传递过来的参数在函数内不可以改变(无意义,因为Var本身就是形参)

       void function(const int Var);

b.参数指针所指内容为常量不可变

      void function(const char* Var);

c.参数指针本身为常量不可变(也无意义,因为char* Var也是形参)

      void function(char* const Var);

d.参数为引用,为了增加效率同时防止修改。修饰引用参数时:

      void function(const TYPE& Var); //引用参数在函数内为常量不可变

这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本, 然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性, 且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙.


(2)const 修饰函数返回值
    const修饰函数返回值其实用的并不是很多,它的含义和const修饰普通变量以及指针的含义基本相同。
    a.const int fun1() //可以防止对函数返回值再次赋值的错误。
    b. const int * fun2() //调用时 const int *pValue = http://www.mamicode.com/fun2();
                          //我们可以把fun2()看作成一个变量,即指针内容不可变。
    c.int* const fun3()   //调用时 int * const pValue = http://www.mamicode.com/fun2();
                          //我们可以把fun2()看作成一个变量,即指针本身不可变。

一般情况下,函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载。通常,不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。原因如下:如果返回值为某个对象为const(const A test = A 实例)或某个对象的引用为const(const A& test = A实例) ,则返回值具有const属性,则返回实例只能访问类A中的公有(保护)数据成员和const成员函数,并且不允许对其进行赋值操作,这在一般情况下很少用到。


[总结]对于非内部数据类型的输入参数,因该将“值传递”的方式改为“const引用传递”,目的是为了提高效率。例如,将void Func(A a)改为void Func(const A &a)

      对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x)不应该改为void Func(const int &x)


4、类相关CONST


(1)const修饰成员变量
const修饰类的成员变量,表示成员常量不能被修改,同时它只能在初始化列表中赋值。
    class A
    { 
        …
        const int nValue;         //成员常量不能被修改
        …
        A(int x): nValue(x) { } ; //只能在初始化列表中赋值
     } 


(2)const修饰成员函数
const修饰类的成员函数,则该成员函数不能修改类中任何成员变量。一般写在函数的最后来修饰。
    class A
    { 
        …
       void function() const; //常成员函数, 它不改变对象的成员变量.                        

//也不能调用类中任何非const成员函数。
}

对于const类对象/指针/引用,只能调用类的const成员函数,因此,const修饰成员函数的最重要作用就是限制对于const对象的使用。

a. const成员函数不被允许修改它所在对象的任何一个数据成员。

b. const成员函数能够访问对象的const成员,而其他成员函数不可以。

 

(3)const修饰类对象/对象指针/对象引用

·             const修饰类对象表示该对象为常量对象,其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。

·             const修饰的对象,该对象的任何非const成员函数都不能被调用,因为任何非const成员函数会有修改成员变量的企图。
例如:
class AAA

    void func1(); 
    void func2() const; 

const AAA aObj; 
aObj.func1(); //错误
aObj.func2(); //正确

const AAA* aObj = new AAA(); 
aObj-> func1(); //错误
aObj-> func2(); //正确

 


补充说明:


     类内部的常量限制:使用这种类内部的初始化语法的时候,常量必须是被一个常量表达式初始化的整型或枚举类型,而且必须是static和const形式,要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现

class A

{…

 enum {size1=100, size2 = 200 };

int array1[size1];

int array2[size2];

}

枚举常量不会占用对象的存储空间,他们在编译时被全部求值。但是枚举常量的隐含数据类型是整数,其最大值有限,且不能表示浮点数。


    如何初始化类内部的常量:一种方法就是static 和 const 并用,在外部初始化,例如:

class A { 

public: A() {} 

private: static const int i; //注意必须是静态的!

};

const int A::i=3;

另一个很常见的方法就是初始化列表:

 class A { 

public: 

     A(int i=0):test(i) {} 

private: 

     const int i;

 }; 



将Const类型转化为非Const类型的方法:

 

采用const_cast 进行转换。  
用法:const_cast <type_id>  (expression) 
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。

·             常量指针被转化成非常量指针,并且仍然指向原来的对象;

·             常量引用被转换成非常量引用,并且仍然指向原来的对象;

·             常量对象被转换成非常量对象。

 


const在c和c++中的区别:


1. 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是一个不能被改变的普通变量,既然是变量,就要占用存储空间,所以编译器不知道编译时的值.而且,数组定义时的下标必须为常量.
2. 在C语言中: const int size; 这个语句是正确的,因为它被C编译器看作一个声明,指明在别的地方分配存储空间.但在C++中这样写是不正确的.C++中const默认是内部连接,如果想在C++中达到以上的效果,必须要用extern关键字.即C++中,const默认使用内部连接.而C中使用外部连接.
(1) 内连接
:编译器只对正被编译的文件创建存储空间,别的文件可以使用相同的表示符或全局变量.C/C++中内连接使用static关键字指定.
(2) 外连接:所有被编译过的文件创建一片单独存储空间.一旦空间被创建,连接器必须解决对这片存储空间的引用.全局变量和函数使用外部连接.通过extern关键字声明,可以从其他文件访问相应的变量和函数.
/* C++代码  header.h */
const int test = 1;
/* C++代码  test1.cpp */
#include "header.h"
using namespace std;
int main() { cout << "in test1 :" << test << endl; }
/* C++代码 test2.cpp */
#include "header.h"
using namespace std;
void print() { cout << "in test2:" << test << endl;}
以上代码编译连接完全不会出问题,但如果把header.h改为:
extern const int test = 1;
在连接的时候,便会出现以下错误信息:
test2 error LNK2005: "int const test" (?test@@3HB) 已经在 test1.obj 中定义
    因为extern关键字告诉C++编译器test会在其他地方引用,所以,C++编译器就会为test创建存储空间,不再是简单的存储在名字表里面.所以,当两个文件同时包含header.h的时候,会发生名字上的冲突.
此种情况和C中const含义相似:
/* C代码 header.h */
const int test = 1;
/* C代码 test1.c */ 
#include "header.h"
int main() { printf("in test1:%d\n",test); }
/* C代码 test2.c */
#include "header.h"
void print() { printf("in test2:%d\n",test); }
错误消息:
test3 fatal error LNK1169: 找到一个或多个多重定义的符号
test3 error LNK2005: _test 已经在 test1.obj 中定义

也就是说:在c++ 中const 对象默认为文件的局部变量。与其他变量不同,除非特别说明,在全局作用域声明的 const 变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。通过指定 const 变更为 extern,就可以在整个程序中访问 const 对象:
      // file_1.cc
      // defines and initializes a const that is accessible to other files
      extern const int bufSize = fcn();
      // file_2.cc
      extern const int bufSize; // uses bufSize from file_1
      // uses bufSize defined in file_1
      for (int index = 0; index != bufSize; ++index)
            // ...

3. C++中,是否为const分配空间要看具体情况.如果加上关键字extern或者取const变量地址,则编译器就要为const分配存储空间.
4. C++中定义常量的时候不再采用define,因为define只做简单的宏替换,并不提供类型检查.