C++(2)_const

const:定义一种变量,它的值不能被改变

参考:《C++ primer》

  • 初始化
    const对象一旦被创建,值不能再改变,所以const对象必须初始化!

    const int i = get_size(); //正确:运行时初始化
    const int j = 42;         //正确:编译时初始化,编译器会在编译过程中用到该变量的地方都替换成对应的值
    const int k;              //错误:k是一个未经初始化的常量

    如果利用一个对象去初始化另一个对象,则它们是不是const都无关紧要!

    int i = 42;
    const int ci = i; //正确:i的值被拷贝给了ci,ci的常量特性仅仅在执行改变ci的操作时才会发挥作用
    int j = ci;       //正确:ci的值被拷贝给了j,无需在意ci是不是一个常量,拷贝完成,新的对象就和原来的对象没什么关系了
  • const对象的有效范围
    默认状态下,const对象被设定为仅在文件内有效。
    当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。

    如果想要让const变量在不同文件中被共享,也就是说,只在一个文件中定义const,在其他多个文件中声明并使用它。
    解决的方法是:对于不管是声明还是定义都添加extern关键字,这样只需定义一次就可以了。

    //file_1.cc 定义并初始化看一个常量,该常量能被其他文件访问
    extern const int bufSize = fcn(); //必须用extern加以限定,使其被其他文件使用
    //file_1.h头文件
    extern const int bufSize; //与file_1.cc中定义的bufSize是同一个,用extern致命bufSize并非本文件所独有,它的定义将在别处出现
  • const的引用
    把引用绑定到const对象上,称之为对常量的引用
    与普通引用不同的是,对常量的引用不能用作修改它所绑定的对象:

    const int ci =1024;
    const int &r1 = ci; //正确:引用及其对象都是常量
    r1 = 42;            //错误:r1是对常量的引用
    int &r2 = ci;       //错误:试图让一个非常量引用指向一个常量对象

    一般来说,引用的类型必须和其所引用的对象的类型一致,但是有例外。

    发现一件有意思的事!

int _tmain(int argc, _TCHAR* argv[])
{
    int dval = 3.14;
    const int &ri = dval;
    dval = 5; //正确:可以通过dval来改变ri的值
    //ri = 5;   //错误:不能通过ri来修改dval的值
    cout<<ri; //输出5
    cout<<dval; //输出5
    return 0;
}

//但是,把dval的类型改为double,两者类型不一致:
int _tmain(int argc, _TCHAR* argv[])
{
    double dval = 3.14;
    const int &ri = dval;
    dval = 5;
    cout<<ri; //输出3
    cout<<dval; //输出5
    return 0;
}
//这是由于在执行const int &ri = dval;时
//编译器内部执行了如下操作
const int temp = dval;
const int &ri = temp;   //将ri绑定在了这个临时量上,是一个const类型

常量引用仅仅对“引用”可参与的操作做出了限定,对于被引用的对象本身是不是一个常量并没有做出限定。
但是,对于一个常量对象,只能用常量引用指向

int i = 42;
int &r1 = i;
const int &r2 = i;  //r2也绑定对象i,但是不允许通过r2修改i
r1 = 0; //正确,r1并非常量,可以修改
r2 = 0; //错误
  • 指针和const
    和引用一样,可以令指针指向一个常量或者非常量;但是对于常量对象的地址,只能使用指向常量的指针
const double pi = 3.14; //pi是一个常量,它的值不能改变
double *ptr = &pi; //错误:试图用一个非常量指针指向一个常量
const double *cptr = &pi; //正确:cptr是一个常量指针,可以指向常量
*cptr = 42; //错误:不能给(*cptr)赋值

double dval = 3.14;
cptr = &dval; //正确:但是不能通过cptr改变dval的值
  • const指针
    指针是对象,而引用不是。
    所以允许将指针本身定义为常量。
    常量指针必须初始化,一旦初始化完成,则存放在指针内的那个地址就不能再改变了。
    即不变的是指针本身而不是指向的那个值。
    指针本身是常量,并不意味着不能通过指针修改其所指向对象的值,能否这样做完全依赖于所指向对象的类型。
int errNumb = 0;
int *const curErr = &errNumb; //curErr将一直指向errNumb
const double pi = 3.14159;
const double *const pip = &pi; //pip是一个指向常量对象的常量指针
  • 顶层const
    顶层const(top-level const)表示指针本身是常量,更一般地,可以表示任意的对象是常量,这一点对任何数据类型都适用;
    底层const(loe-level const)表示指针所指向的对象是常量,更一般地,与指针和引用等复合类型有关

    int i = 0;
    int *const p1 = &i; //top-level
    const int ci = 42; //top-level
    const int *p2 = &ci; //low-level
    const int *const p3 = p2; //靠右的const是top-level,靠左的const是low-level
    const int &r = ci; //low-level
    
    //执行对象的拷贝操作
    //top-level不受影响,执行拷贝并不会改变被拷贝对象的值
    i = ci; //正确
    p2 = p3;//正确:p2和p3指向同类型对象,所以p3的top-level不影响
    //low-level有限制,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须可以互相转换
    int *p = p3; //错误:p3包含low-level的含义,而p没有,万一通过p来修改p3就不行了
    p2 = p3; //正确
    p2 = &i; //正确:int*可以转换成const int*,即会把i的地址临时存成const int* temp后拷贝给p2
    int &r = ci; //错误:不能将const int转成int
    const int &r2 = i; //正确:int可以转成const int
  • constexpr和常量表达式
    常量表达式是指在编译过程中就能得到计算结果的表达式。

    const int sz = get_size(); //不是常量表达式,sz本身虽然使常量,但具体值要到运行时才能获得

    constexpr变量
    C++新标准规定。
    一般来说,如果认定一个变量时一个常量表达式,那就把它声明为constexpr变量。constexpr类型由编译器来验证变量的值是否是一个常量表达式。
    新标准还允许定义一种特殊的constexpr函数。这种函数应该足够简单到编译时就能得到结果,这样就能用constexpr函数来初始化constexpr变量。
    必须明确的一点是,在constexpr声明中如果定义了一个指针,那么限定符仅仅对指针有效,与指针指向的对象无关,因为constexpr将它所定义的对象置为top-level

const int *p = nullptr; //p是一个指向整型常量的指针
constexpr int *q = null; //q是一个指向整型的常量指针
constexpr const int *p = &i; //p是一个指向一个整型常量的常量指针
constexpr int *p1 = &j; //p是一个指向整型的常量指针

Summary:

名称定义示例
常量引用仅限定了该“引用”所参与的操作,means 不能通过常量引用来改变它所引用的对象const int &ri = temp; //temp是不是const变量都无关
指向常量的指针仅限定了该“指针”所参与的操作,means不能通过常量指针来改变它所指向的地址的值const double *cptr = &temp; //temp是不是const变量都无关
const指针将指针本身定义为常量,必须初始化,一旦初始化后,指针内存放的地址不能再改变int *const curErr = &errNumb; //curErr将一直指向errNumb

想要弄清楚这些声明的含义最有效的方法是从右往左阅读。
指针本身是不是常量和指针指向的是不是常量,是两个独立的问题。
2.4节需要好好研读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值