const 和 constexpr

修饰符const

   const 关键字 让被修饰的对象为只读,不可以修改其值。

应用场景

   const的应用场景主要有三种,第一种是修饰普通变量,第二种是修饰指针或引用,第三种是修饰成员函数。

常变量

   可以读取变量,不可进行写入操作。


//可以直接用常量初始化
const int a = 6;  
a = 9;    //错误, a 是可读类型

//可以用变量初始化
int b = 3;
const int c = b;

  像这种情况,const 修饰的变量只能定义时初始化进行赋值,且必须初始化。

const int d; //错误,未进行初始化
const int d = 5; //正确,进行了初始化

指针和引用

  先来介绍两个概念:

  1. 顶层const: 不可修改对象的值
  2. 底层const: 不可修改指向对象的值
int a = 5;  
int b = 6;  

const int *p = &a; //这个是底层const,无法修改指向对象的值  
*p = 15;   //错误,无法修改指向对象的值  
 a = 15;    /* 这是正确的,const 只是无法通过 p 对象 来修改指向 a 对象的值,  
            但通过 a 对象本身可以进行修改 */
 p = &b;    //这个是允许的,可以修改 p 所指向的对象  

int *const q = &a; //顶层const,无法修改对象的值  
q = &b;    //不可以修改 q 对象的值  
*q = 20;   //这个是可以的,其指向的对象没有限制  

  对于引用来说,它本身不是对象,只是对象的别名,像标签一样,与其指向的对象绑定在一起,但 const 修饰之后 又有一些特例:

/* 可以绑定非常量对象 */
int d = 2;
const int &p = d;

/* 可以绑定常量 */
int &a = 0;   //错误
const &a = 0;  //正确

/* 可以绑定常量表达式 */
int b = 1;
int &c = 2*b;   //错误
const int &c = 2*b;   //正确

/* 其实对于对于常量引用来说,只要等号右边可以转化为引用所指定的类型,都可以进行初始化 */

成员函数

  对成员函数用 const 修饰,意味着在这个函数内对 类的任何成员不能进行修改。

#include <iostream>

class Entity {
private:
    int a;
    int b;

public:

    int get() const {
        //a++; // 这是错误的,const 修饰意味着不能对类成员进行修改
        return a;
    }
    
};

void put(const Entity &x) {
	std::cout << x.get() << std::endl;
}

int main() {
	Entity e;
	put(e);
}

  上面的程序显然是对的,那么对其进行小的修改:

#include <iostream>

class Entity {
private:
    int a;
    int b;

public:

    int get(){  //删除了const    
	return a;
    }
    
};

void put(const Entity &x) {
	std::cout << x.get() << std::endl;  /*报错,因为参数类型 const 修饰
										而get()汉书可能修改 x,尽管它并未实现,
										但它存在实现的可能*/
}

int main() {
	Entity e;
	put(e);
}

  稍微总结一下就是,const 修饰的成员函数,保证了该函数无法对类的任何成员进行修改。这种修饰是绝对严格的,即使你并未对其进行修改,但存在可能性,就会报错,举一个例子:

#include <iostream>

void putnum(int &x) {    /*将其改变为putnum(const int &x) 或 putnum(int x) 或
                          putnum(const x) 便正确了,排除修改成员变量的可能*/

};
class Entity {
private:
    int a = 1;
    int b = 2;

public:

    int get() const{
        
        putnum(a);    /*这里报错了,因为putnum()函数存在修改成员变量a的可能,
                      即使什么都没做*/
        return a;
    }

};

void put(Entity& x) {
    std::cout << x.get() << std::endl;
}

int main() {
    Entity e;
    put(e);
}

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

  这是一个补充知识点:如果有多个文件的话,const 修饰的变量只能在被定义的编译单元中使用,没有可共享性,也就是内部链接。想要实现const 对象在不同编译单元间的共享,需要在定义和声明前都加上 extern 关键字,注意,是定义和声明都需要 extern 关键字。

/* 在main.cpp中 */
const int e = 7;   //错误,这种定义是不正确的

extern const int e = 7;    //正确
/* 在fuc.cpp中*/
extern const int e;


constexpr 和 常量表达式

  常量表达式:指的是值不变且在编译时就知道结果。

const int a = 1;         //是常量表达式
const int b = a + 1;     //是常量表达式
int c = 3;           //不是常量表达式,可修改其值
const int d = get(); //不是常量表达式,在运行时才知道结果    

constexpr 变量

  变量,可用constexpr修饰来让编译器检测变量值是否为常量表达式,声明为constexpr的变量一定是一个常量,且用常量表达式初始化。

constexpr int a = 1;
constexpr int b = a + 1;
constexpr int c = fuc(); //只有当fuc()是constexpr函数时,才是正确声明

constexpr指针

  constexpr指针只是对指针本身进行限制,对指向对象不起作用。

int a =5;
constexpr int *p = &a;   //指针是常量,不可修改
const int *q = &a;       //指针指向对象是常量,不可修改
posted @ 2024-04-28 23:34  KK_LOVE  阅读(110)  评论(0)    收藏  举报