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置为了顶层。

猜你喜欢

转载自blog.csdn.net/weixin_44848852/article/details/123977764