当前位置: 首页 > Linux

得力的C++阅读笔记(二)

时间:2023-04-06 19:51:47 Linux

这几天忙着换工作,更新的效率一直没有保证。既然决定了,就不能拖了。从今天开始,一定要保证3天一篇文章的速度。好吧,让我们继续学习EffectiveC++。建议03:使用const关键字尽可能使用const此建议应作为信条使用。在阅读AmazonAlexa和GoogleGrpc源码的过程中,我发现一流的厂商在这一点上都做到了相当不错。撇开它的语法保护(只读)不谈,它也很好地告诉阅读你代码的人这个对象/函数是只读的,不应该被修改。它是一种非常有效的语义约束,不仅对编译器有效,而且对开发人员也能进行不必要的修改。既然一个对象应该是只读的,那我们为什么不给它加上一个const,进一步保证它不被修改呢?编译器将对所有const修改的对象执行编译时检查,以确保没有违反约束。首先总结一下可以使用const的场景。const可用于修饰几乎所有你能想到的对象,包括:常量(全局或命名空间中)文件函数块作用域(blockscope)声明为静态对象类内部成员变量指针和指向Const的指针关于指针和指针的语义很常见(在面试中可能会被问到),例如:charstrs[]="HelloWorld!";字符*p=strs;//普通指针constchar*p=strs;//一个指向字符常量的指针(非常量指针,常量数据)char*constp=strs;//一个字符指针常量(非常量指针,非常量数据)charconst*p=strs;//一个指针toacharacterconstant(non-constpointer,constdata)constchar*constp=strs;//Apointerconstanttoacharacterconstant(constpointer,constdata)好像有点乱,不过只要观察是否const在解引用字符(*)的左边或右边,可以判断是指针常量还是指向常量。如果const位于解引用字符的左侧,则表示指针指向一个常量值。如果const在解引用字符的右边,则表示该指针是一个常量,指向一个不可修改的指针。如果解引用符号的左右两侧有const修饰符,则表示该指针是指向常量的指针常量。请用下面这句话简写:Lvaluerightdirection在左边表示指向的值不可修改,在右边表示指向的值不可修改。按照习惯,有些程序员在修改常量指针(constdata)时,习惯把const写在类型的左边:voidfunc(constchar*p);(字符常量*p);这两种写法在功能上是一样的,没有本质区别。在某些情况下,我们常常希望遍历一个容器。我们可以使用STL迭代器来完成遍历,但是我们不想引起容器值的变化。这时候我们可以使用const_iterator来迭代:std::vectorvec;..std::vector::const_iteratoriter=vec.begin();for(iter;iter!=vec.end();++iter){std::cout<<*iter<vec;...conststd::vector::迭代器iter=vec.begin();*iter=10;//true指向可以修改的内容++iter;//报错,指向不能修改的情况很少见,既然用了迭代器,不让Iterator移动的可能性不大,但是写法还是需要的要注意。一般情况下,我们在移动迭代器时使用++iter而不是iter++,因为++iter是一个左值,它已经是一个对象,而iter++是一个右值,一个引用对象的表达式,返回一个临时变量,效率较低。有大佬点评返回值的constification,有点激动。前面的描述有问题。一般来说,函数的返回值是不构造的。下面可能会举出返回值需要const修饰的情况:函数的返回值应该是常量。为了使重载的运算符符合逻辑,运算符重载和const约束对a+b+c这样的操作没有影响,因为a+b操作的结果是const,但它只是一个只读操作,并且是一个new类型A将被创建并返回。通过函数创建一个指向常量的指针。如果通过函数创建常量字符串,除了main函数中的约束外,还可以在函数返回类型中约束对const成员函数的调用。const类型的对象不能调用自己的非const成员函数,但可以调用自己的const成员函数。当const成员函数的返回类型为引用时,需要添加const约束。一般情况下,让函数返回一个只读值,可以减少客户错误导致的意外或未知数。BUG,也能保证安全和效率。例如,在有理数运算符重载的实现中,将返回值修改为只读,可以防止用户对返回值进行一些奇怪的操作而产生意想不到的结果。classRational{...};constRationaloperator*(constRational&lhs,constRational&rhs);//有理a,b,c;...(a*b)=c;//这种情况很少见if(a*b=c)...//少了一个等号导致判断结构不符合这种问题,排错可能会浪费时间,但是如果返回值声明为const,可以从根源上切断这种错误的发生。const成员函数将成员函数声明为const有两个优点。首先,界面的使用者可以知道界面是否会改变对象的内容。其次,const对象使得“操作const对象”成为可能。所谓“操作const对象”,就是尽可能的避免传值带来的复制开销,而是要代替const对象的传递(reference-to-const),而为了达到这种方式,前提就是有一个const成员函数来处理const对象。关于成员函数的常量性质,有两个概念:按位常量(物理常量)和逻辑常量。Bitwiseconst表示成员函数不直接修改对象中的任何位。这个概念的好处是编译器可以很容易的检查出违规点,只需要检测成员变量的赋值语句就可以了。这正是C++目前使用的方式,但存在一个问题,即对象虽然不会在函数中以任何方式被修改,但它的返回值或动作间接导致了修改,比如返回一个非常量值,对象外部可以通过修改这个值来修改,比如:constCTextBlockcctb("hello");//声明一个常量对象char*pc=&cctb[0];//调用constoperator[]获取对应的下标指针(假设运算符已经重载)*pc='H';//对象还是被修改的,所以还有一个逻辑constness的概念,就是允许在const成员函数中修改对象的部分内容,比如a文本编辑器需要获取当前文本长度,所以它实际上并不修改文本内容,但文本长度可能会实时变化。这时,我们声明一个:std::size_ttl;std::size_tTextpanel::length()const{...tl=std::strlen(text);//错误constdoesnotallowmembervariableassignmentreturntl;}此时我们可以使用mutable创建一个const相关的swing域来让tl可以修改,只需将tl声明为:mutablestd::size_ttl;并且可以修改。虽然编译器使用的是bitwiseconst,但是我们在实际编程中应该使用logicalconstness进行开发。当类的non-const版本的成员函数等价于const版本的实现时,为了减少重复代码,可以使用const_cast和static_cast扩展non-const版本const版本的:classTextBlock{public:constchar&operator[](std::size_tposition)const{...returntext[position];}char&operator[](std::size_tposition){returncons_cast(static_cast(*this)[position]);}};简介:将某些内容声明为const可以帮助编译器检测错误的用法。const可用于修饰对象、函数参数、函数返回类型和任何作用域函数体中的成员。编译器强制执行按位常量,但我们在开发时应该使用概念常量。当const和non-const成员函数实现相同时,可以在non-const版本中调用const版本,Transformation减少代码重复。