c++ const详解

发布时间 2023-06-01 12:32:08作者: 白菜茄子
  • 可以使用const的地方就尽量使用const
    • 一般引用的类型必须与其所引用对象的类型一致,但是允许一个常量引用绑定到非常量的对象、字面值,甚至是一个一般表达式
double a = 42.0;
int &b = a;//编译错误,一般引用类型需要与所引用对象类型一致
int a = 42;
const int &b = a;
a = 43;
cout<<a<<" "<<b<<endl;//43 43
//虽然b是const,不能被修改,但是却可以绑定到一个非const变量上,这个非const变量可以改变,从而使得对应的const变量也发生改变
double a = 42;
const int &b =a;
a = 43;
cout<<a<<" "<<b<<endl;//43 42
//这是因为二者的类型并不一致,在这种情况下,b实际绑定的是一个临死变量,所以此时修改a的值,b并不会发生改变
const int temp = a;
const int &b = temp;

指针和const

  • 指向常量的指针:不能用于改变其所指对象的值
  • 常量指针:不能改变指针本身,必须初始化(因为指针本身不可变,所以一定需要初始化)
int a = 42;
int d = 42;
const int *b = &a;//指向常量的指针
int* const c = &a;//常量指针
//    *b = 43;//编译错误
//    c = &d;//编译错误
  • 理解const和指针声明定义的意义可以从右向左进行阅读
    • 从右向左看,先遇到的是一个const,说明b本身是常量对象,对象的类型由声明符的其余部分确定
    • 声明符的下一个是*,说明b是一个常量指针,剩下的const int说明指向的内容是一个int常量对象
int a = 42;
const int* const b = &a;

顶层const和底层const

  • 顶层const:一个指针本身添加const限定符
  • 底层const:指针所指向的对象添加const限定符
  • 底层const只与指针,引用等符合类型的基本类型部分有关
  • 一般顶层const可以表示任意的对象是常量
int i = 0;
int * const c = &i;//顶层const
const int a = 42;//顶层const
const int*b = &a;//底层const
  • 拷贝时,顶层const无影响,但是底层const必须相同,或者左边是常量,右边是非常量
int i =0;
const int *b = &i;//底层const
int* const c = &i;//顶层const

//    int*d = b;//编译错误,因为左边是非底层const,右边是底层const
int *e = &i;
const int *f = e;//可以,左边是底层const,右边是非底层const

int *g = c;//顶层const无影响
  • 其实这里的顶层const和底层const在拷贝时的影响可以理解为
    • 不能将原先不能修改指向对象的指针,赋值给一个可以修改指向对象的指针.

constexpr和常量表达式

  • 常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式
  • 字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式
  • 一个对象是不是常量表达式由它的数据类型和初始化共同决定
const int max_files = 20;//常量表达式
const int limit = max_files + 1;//常量表达式
int x = 7;//非常量表达式
const int sz = get_size();//取决于get_size()是不是constexpr函数
  • 将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式
constexpr int mf = 20;
constexpr int limit = mf + 1;
constexpr int sz = size(); //只有当size()是一个constexpr函数时才是一条正确声明语句
  • 一般如果认定变量是一个常量表达式,就把它声明成constexpr类型
  • 在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关
constexpr int *q = nullptr;//常量指针

constexpr函数

  • constexpr函数是指能用于常量表达式的函数
    • 函数的返回类型及所有形参的类型都得是字面值类型
    • 函数体中有且只有一条return语句
    • 可以包含其他语句,但不能是运行期语句,只能是编译器语句
  • 编译器会将constexpr函数视为内联函数,所以在编译时若能求出其值,则会把函数调用替换成结果值
constexpr int size(int x){
    return x+10;
}
constexpr int i = 10;
int x = 10;
constexpr int sz = size(i);//正确
constexpr int sz1 = size(x);//编译错误

const成员函数

class A{
public:
    string str;
    A(string str):str(str){}
    void print(){
        cout<<str<<endl;
    }
};
int main(){

    const A a1("sjq");
    a1.print();//编译错误
    return 0;
}
  • 需要将print设置为const成员函数
class A{
public:
    string str;
    A(string str):str(str){}
    void print() const{
        cout<<str<<endl;
    }
};

int main(){
    const A a2("sjq");
    a2.print();
    return 0;
}
  • 这是因为每一个成员函数其实否会有一个默认的this指针,这个指针指向的就是调用这个函数的对象,一般而言这个this指针是非const的,而此时a1是const,则传递时候就是传递的一个const this,所以此时会报错
  • 如果成员函数同时具有const和non-const两个版本的话,const对象只能调用const成员函数,non-const对象只能调用non-const成员函数,但是如果只有const成员函数,则non-const对象可以调用const成员函数,这里涉及的是一个最佳匹配
  • 但是需要注意的是const成员函数因为this是const的,所以不能对成员变量进行修改