const限定符

Posted kongyijin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了const限定符相关的知识,希望对你有一定的参考价值。

2.4 初始化和const

  • const对象一旦创建后其值就不能再改变,所以const对象必须初始化,并且可以是任意复杂表达式。
  • 如果利用一个对象去初始化另一个对象,则它们是不是const都无关紧要,例如:
int i = 42;
const int ci = i;     //正确:i的值被赋给了ci
int j = ci;       //正确:ci的值被赋给了j

  为什么呢,因为ci的常量特征仅在执行改变ci的操作时才会发挥作用(不允许修改ci的值),注重的是过程,而与到底是谁,是哪种类型初始化ci没有关系。

  • 默认状态下,const对象仅在文件内有效

   解决办法是,对于const对象,不管是在文件中声明还是定义都要添加extern关键字。

//file.c 定义并初始化了一个常量,并且该常量能被其他文件访问
extern const int bufsize = fcn();
//file.h  头文件
extern const int bufsize;

2.4.1  const的引用:对常量的引用

与普通引用(引用类型必须与引用对象类型一致)不同,常量引用:

  • 在初始化常量引用时 ,允许用任意表达式作为初始值。
  • 允许为常量引用确定非非非非非非常量的对象,字面值,甚至是一般表达式。

两种经典错误:

(1)

const int ci = 1024;
const int &r1 = ci;

r1 = 42;               //错误,常量引用r1不允许修改常量   
int &r2 = ci;     //错误:ci是常量不允许修改,但是r2是普通引用,存在修改ci的风险

不能用非常量的引用指向一个常量!!!

(2)

double dval = 3.14;
const int &ri = dval; 

ri引用了一个int类型的数,其对象也应该是int类型的,但是dval却是一个浮点数。因此为了确保让ri能够绑定一个整数,编译器把上述代码变成了如下:

const int temp = dval;  //让浮点数生成一个临时的整型常量
const int &ri = temp;    //让ri绑定这个临时的常量

乍一看其实没啥毛病,但是仔细想想,我的初心就是想让ri绑定dval,让ri能够修改dval的值,但是从底层来看,ri其实绑定了temp,也就是说无论我怎么修改ri,dval的值都不会被修改。那这样就没有意义了,所以这个代码运行是没问题的,但其实是非法的。

对const的引用可能引用一个并非const的对象

  必须认识到,常量引用仅仅对引用可参与的操作做出了限定,对于引用的对象本身是不是常量并未做限定,例如:

int i = 42;
int &r1 = i;           //引用r1绑定i
const int &r2 = i;    //常量引用r2也绑定i,但是不允许修改i的值
r1 = 0;          //允许                
r2 = 0;          //不允许

代码中,不允许通过修改r2修改i的值,尽管如此,i的值仍然可以通过其他途径修改(r1,i)。

 

2.4.2 指针和const

这部分内容,请看上一篇博客(引用和指针的区别)。

2.4.3 顶层const

如前所述,指针本身是一个对象,但是它又可以指向另外一个对象。因此存在两个独立的问题:指针本身是不是常量;指针所指的对象是不是常量。

  • 顶层(top-level const):指针本身是一个常量
  • 底层(low-level const):指针所值的对象是一个常量

 2.4.4 constexpr和常量表达式

常量表达式(const expression)是指: 值不会改变并且在编译过程中就能得到计算结果的表达式。

 get_size()函数 -- 直到运行时才能获取到,所以也不是常量表达式

 

 

2.4 const限定符

2.4 const限定符

关键字const对变量的类型加一限定

const int bufSize = 12;

const所修饰的对象一旦创建后就不能改变其值,所以其修饰的对象必须要初始化。
与非const类型对象相比,const所修饰的对象可以进行大部分非const对象所进行的操作,但是不能被修改或者重新赋值。

默认状态下,const对象只在文件中有效

当以编译时初始化一个const修饰的变量时,编译器将在编译过程把用到该变量的地方都变成对应的值。

(《C++ Primer》)

2.4.1 引用常量

引用常量不能修改它所绑定的对象,非常量引用不能指向常量对象

int i = 10;
const int &r = i;
r = 11; // 错误:表达式必须是可修改的左值
const int i  = 10;
int &r = i; //错误:将 "int &" 类型的引用绑定到 "const int" 类型的初始值设定项时,限定符被丢弃

初始化和对const的引用

引用的类型必须与所引用的对象类型一致,例外:
初始化常量引用时允许用任意表达式作为初始值,只要表达式的结果能够转换称引用类型即可。

int i = 10;
const int &r = i; // 允许常量引用绑定到一个普通int对象上
const int &r1 = 45; // 正确:r1是一个常量引用
const int &r2 = r*2; // 正确:r2是一个常量引用
int &r3 = r1*2; // 错误:r3是一个普通的非常常量引用

对const的引用可能引用一个并非const的对象

常量引用仅对引用可参与的操作做出了限定,对于引用的对象本身是不是一个常量未做限定。因为对象也可能是个非常量,所以允许通过其它途径改变常量引用的值。

int i = 10;
const int &r = i;
i = 15;
cout << r << endl; // 15
int &r1 = i;
r1 = 16;
cout << r << endl; // 16

2.4.2 const和指针

指针可以指向常量或非常量,要想存放常量对象的地址的指针必须是被const关键字修饰的,指向常量的指针不能用于改变其指向的对象的值。

const double i = 3.14; // 常量对象
const double *p = &i; // 正确:p是指向常量的指针
*p = 12; // 错误:指向常量的指针不能用于改变其指向的对象的值
double *p1 = &i; //错误:"const double *" 类型的值不能用于初始化 "double *" 类型的实体

指向常量的指针的类型必须和指向的对象类型一致,例外:
指向常量的指针指向了非常量的对象

int i = 10;
const int *p = &i; // 正确:常量指针指向了非常量对象
 i = 15; // 可以通过修改非常量对象的值来修改指向常量的指针
 cout<< *p << endl; // 15

const指针

指针是对象而引用不是,因此就像其它类型对象一样,允许将指针本身定位常量。常量指针(const pointer)必须初始化,一旦初始化完成,则指针的值(存放的地址)也就不能被改变了。

const int i = 10;
const int *const pip = &i; // pip是一个指向常量的常量指针

	int i = 15;
    int *const p = &i;
    cout << *p << endl;//15
    *p = 45;
    cout << *p << endl;//45
    i = 10;
    cout << *p <<endl;//10
    int k = 33;
    p = &k; // 错误:常量指针所存放的地址不可以被改变

指向常量对象的指针必须是const修饰的指针,指向常量的指针可以存放非常量对象也可以存放常量对象的地址,const指针存放的地址固定不变。

2.4.3 顶层const

指针本身是对象,指针本身是不是常量和指针指向的对象是不是常量是两个独立的问题,为了区分这两个说法,用顶层const底层const来区别。

类型顶层const底层const
解释指针是常量对象指针指向一个常量对象
示例int *const p = &i;int const i = 10;const int *p =&i;

拓展顶层const和底层const的定义:顶层const指的是任意常量对象,而底层const则与指针和引用等复合类型的基本类型部分有关。

int i = 10;
int *const p1 = &i;// 不可以修改p1的值,这是一个顶层const
const int *p2 = &i; // 可以修改p2的值,这是一个底层const
const int ci = 12;// 不可以修改ci的值,这是一个顶层const

当执行拷贝操作的时候,顶层const和底层const有明显区别。顶层const不受影响,因为顶层const所修饰的对象是常量对象,不可被修改,执行拷贝操作并不会改变拷贝对象的值,因此拷入和拷出的对象是否是常量都无影响。

 int i = 11;
    const int ci = 10;

    const int *p1 = &ci; // p1:底层cosnt
    const int *const p2 = p2; // 第一个const是底层const,第二个是顶层const
    i= ci; // 正确:拷贝ci的值,ci是一个顶层const对此操作无影响
    p1 = p2; // 正确:p2和p3指向的对象类型相同,p3顶层const的部分不影响

对于底层const,拷入和拷出的对象必须具有相同的底层资格

const int ci = 10;
const int *p2 = &i; 
int *p = p2; // 错误:p2是底层const,而p无底层const资格

2.4.4 constexpr和常量表达式

常量表达式是指值不会改变并且在编译过程中就能得到计算结果的表达式。字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。一个对象或表达式是不是常量表达式由其数据类型和初始值共同决定。

const int  i = 10;// i是常量表达式
const int  l = i +1; // l是常量表达式
int s = 2;// s不是常量表达式,其数据类型只是一个普通的int
const int  k = get_size();// k不是常量表达式,因为常量k的初始值是要运行函数之后才能够获取到

constexpr变量

C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为cosntexpr的变量一定是一个常量,且必须用常量表达式初始化。

constexpr int i = 20; // 20是常量表达式
constexpr int l = size(); // 当size函数是一个cosntexpr函数时才是一条正确的语句

constexpr变量

声明constexpr时用到的类型成为“字面值类型”,算术类型、引用类型和指针都属于字面值类型,自定义的类、IO库、string类型都不属于字面值类型。
constexpr指针的初始值必须时nullptr或者0,或者是存储于某个固定地址中的对象。
函数体内的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量,而定义于所有函数体外的对象固定不变的地址,可以用来初始化constexpr。

constexpr和指针

在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关

const int *p = nullptr; // p是一个指向整型常量的指针
constexpr int *q = nullptr; //q是一个指向整数的常量指针

constexpr将所定义的对象q置为了顶层。

以上是关于const限定符的主要内容,如果未能解决你的问题,请参考以下文章

const限定符

const 不兼容的类型限定符问题

const限定符用法汇总

2.4 const限定符

const限定符

函数名称后不久的 const 类型限定符[重复]