当前位置: 首页 > 科技观察

一篇讲解C-C++Const-Const_Cast-Constexpr

时间:2023-03-12 00:26:27 科技观察

本文转载自微信公众号“码砖杂工”,作者我不想种田。转载本文请联系码砖手公众号。很多人对const、const_cast、constexpr的用法一头雾水,一头雾水地使用。一般来说,即使你乱用,问题也不是很大,因为如果你犯了一个大错误,它就会崩溃。如果它崩溃了,它会自然得到纠正。但是作为一个有志向的专业程序员,应该是闻之即兴,思之所想。一、constC语言中const的用法先说const,不知道怎么翻译这个东西。C语言中this关键字的用法比较简单,大概有以下几种用法:[1]修改普通变量:变量是只读的,程序运行时不能修改。constinti=100;//iisreadonlyi=200;//compileerror,variableicannotassignable[2]modifiedpointerconstT*p:表示p指向的对象内容不能通过p修改,反之const成员类T的只能通过p函数调用conststructFoo*f=newFoo;f->dataX=100;//compileerrorconstchar*p="abc";p[1]='x';//compileerrorf->nonconst_member_function();///compileerror(later)[3]ModifiedpointerT*constp:表示该指针只能在初始化时设置,之后不能修改。chars1[]="abc";chars2[]="xyz";char*constp=s1;p=s2;//compileerror[4]修改指针constT*constp:表示p指向的对象不能被已修改,并且p的指向无法更改。constchar*constp="abc";p[1]='B';//compileerrorp="xyz";//compileerror[5]修饰函数参数:c语言中的const修饰参数所体现的含义与以上总结:C语言中,const的用法几乎相同,比较简单。C++扩展了const的用法[1]修饰成员变量:const成员变量只能在初始化列表中初始化,在程序运行过程中不能修改;如果是const整型,可以按照C++11标准直接初始化。structFoo{Foo():PI(3.15){}//PIisinitializedbyinitializerlistconstintc=100;//C++11supportconstfloatPI;};[2]修饰成员函数:表示该成员函数为只读函数,不会修改默认参数this如果修改了成??员变量,编译时会报错。classFoo{intm_money;public:intget_money()const//?{returnm_money;}intset_money(intmoney)const//?{m_money=money;//修改this->m_money;需要去掉函数const修饰}};[3]修饰引用:引用是C++特有的语法特征。引用是别名,本质上类似于指针,因此const修饰的引用与修饰的指针具有相似的语义和约束。Foof;constFoo&r=f;r.m_data=1;//compileerror[4]C++中const修饰指针的补充intmain(){constFoo*f=newFoo;f->const_member_function();//OKf->non_const_member_function();//compileERRORreturn0;}为什么?因为const成员函数相当于promise不去修改this的成员变量,并且这个promise会被编译器检查,如果promise没有实现,编译器就会报错。而constFoo*f表示f指针指向的变量的内部值不能通过f修改。肯定不能用f->data=1。另一方面,你只能通过f调用它的const成员函数,因为const成员函数的语义是不会修改this的值,编译器可以很容易地进行这个检查。const修饰参数const可以修饰普通参数,也可以修饰指针/引用参数。因为形参是实参的拷贝,const修饰普通参数是没有意义的。我们将重点关注const修饰的指针/引用参数。例如标准C库函数strcpy的签名:char*strcpy(char*dst,constchar*src);dst代表目标地址,src代表源字符串,const修饰源字符串。这是因为源字符串被复制到目标字符串。无需修改源字符串内容,相当于给strcpy调用者一个承诺:放心大胆调用,strcpy函数的实现保证src内容不被修改,编译器将执行此检查。这样在review代码的时候,如果想跟踪src哪里被修改了,看到strcpy的签名时,就不用打开函数看实现了。只要不违背承诺,绝对不是这个函数里改的src。constchar*src是一个承诺和约束。在调用的地方,constchar*形式的形参既可以传constchar*实参,也可以传char*实参,因为constchar*是一个更强的promise。但反之则不然。比如第一个参数dst不包含const,那么如果有constchar*p的变量类型,那么p就不能作为第一个参数传入strcpy,编译就会失败。因为strcpy不承诺不修改dst,它是一个较弱的承诺。只有声明为const指针的参数才能传递const指针实际参数。const其他的const也可以修饰返回值,也可以和extern结合使用,不过这些都是一些语法小技巧,一般开发中不常用到,遇到了再查也不迟。2、const_castconst_cast有什么用?const是C++的强制转换,用于去除const属性,如:Foofoo;constFoo*f1=&foo;Foo*f2=const_cast(f);Foo*f3=(Foo*)f;const_cast的作用几乎和强制转移一样。C++添加const_cast主要是为了功能完整性。Const_cast作用于引用就像它作用于指针一样。为什么说const_cast几乎都是反映接口设计的问题呢?程序设计必须言行一致,信守承诺,即:参数不能声明为const指针,在函数实现中强行去掉const属性。首先,这样做很危险。例如,constchar*p="abc";p指向一个作为参数传递的常量字符串,如果强行转换+修改,会导致程序崩溃。其次,这样做是split,因为加const修饰相当于让编译器给你做检查,这样当你违背承诺的时候,会通过编译时检查提醒你错误,但是当它真正报错给你,你说你别管,我就是想鲁莽一下。const_cast或通过c样式强制转换基本上暴露了设计问题。设计良好的程序基本上不需要constcasts。因为const约束会在调用链中传播,所以需要始终如一地遵守约定,找到导致const被强制转换的错误源头。这可能需要多一点时间,但这是值得的。3.constexprconst不区分编译时常量和运行时常量。constexpr是C++11提出的关键字,仅限于编译器常量。其含义与14版本有些不同,C++11中constexpr指定的函数返回值和参数必须保证为字面值,返回码必须只有一行,带来更多的限制对函数设计者来说,比如通常只能通过返回三元运算符+递归来计算返回的字面值。在C++14中,只要保证返回值和参数都是字面量值,就可以在函数体中加入更多的语句,方便更灵活的计算。这里主要说一下constexpr和const的区别。constexpr可用于修改变量、函数和构造函数。一旦上面的任何一个元素被constexpr修饰,就相当于告诉编译器“请大胆的把我当做一个在编译时可以获取常量值的表达式来优化我”。constexprfunc(){return10;}intmain(){intarr[func()];}在编译时大胆优化func(),在编译时确定func计算出的值10,而不用等到运行时计算。这就是constexpr的第一个作用:让编译器有足够的信心在编译时优化constexpr修饰的表达式。constexpr还有一个特点。虽然它的一个作用是希望程序员能给编译器优化的信心,但它猜测可能是被程序员蒙骗了,编译器也不会因此“生气”。”Abortcompilation.4.结论C/C++程序应该主动使用const/constexpr,什么叫主动使用?只要有可能,那我们就应该使用const/constexpr。只要有可能,就应该使用xx这种话一般是错误的,但是用在const/constexpr里面是正确的,因为使用const/constexpr基本上会让你的程序更健壮更快,而且整个程序被const修饰的类型变量,当gcc打开优化选项的时候,可能会直接编译变成汇编代码指令,而不是生成一个变量,constexpr的优化作用在上一节已经解释过了,对应的是:只要有可能,就不要用const_cast,基本反映了接口设计的问题,相信吧或不,这取决于你!