韶关专业网站建设教程,网站架构工程师,entware wordpress,外包公司属于劳务派遣吗Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk
(๑•́ ₃ •̀๑) 文章专栏#xff1a; C之旅 const是个奇妙且非比寻常的东西#xff0c;博主从《Effective C》一书中认识到关于const更深层次的理解#xff0c;写此博客进行巩固。 #x… Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk
(๑•́ ₃ •̀๑) 文章专栏 C之旅 const是个奇妙且非比寻常的东西博主从《Effective C》一书中认识到关于const更深层次的理解写此博客进行巩固。 const的作用
const可以指定一个“不该被改动的对象”编译器会强制实施这个约束const可修饰类外的作用域(全局域,命名空间域等)中的常量文件函数或静态常量等const可修饰类内的static和non-static成员 const修饰指针和迭代器 const修饰指针
const修饰指针无外乎三种
char greet[] hello;
char* pg greet;//指针指向内容不可变
const char * pg greet;
char const * pg greet;//指针本身不可变
char * const pg greet;//指针本身和指针执行内容都不可变
const char * const pg greet;
总结
1.const出现在*号左边时,表示指针指向的内容是常量。
2.const出现在*号右边时,表示指针本身是常量。
3.const出现在*号左右边时,表示指针本身和指针指向的内容都是常量 const与迭代器
声明迭代器为const
我们把迭代器看作一个类型它的作用就像个T*的指针当声明迭代器为const时类比const int是int这个int类型变量不可能此时说明迭代器类型变量是不能变的也就是类比修饰一个T* const的指针。
vectorint v {4,1,2,9,10};
const vectorint::iterator it v.begin();it; //错误迭代器类型对象不可变
(*it); //正确指向内容可以变
我们也可以利用下面代码进行验证当生成解决方案时会对其进行报错
templateclass T
void fun()
{const T x;cout typeid(x).name() endl;x;
}
int main()
{//op o;//cout o[1];vectorint v {3,5};vectorint::iterator it v.begin();;funvectorint::iterator();return 0;
}
const迭代器 如果你希望迭代器所指的东西不可被改动(也就是类似希望模拟个const T*指针)你需要的是const_iterator.
std::vectorint v {1,5,2,3,4};std::vectorint::const_iterator it v.begin();*it 10; //错误此时是const迭代器指向内容不可改变。
it; //此时不是声明迭代器为const本身迭代器可以改变。
注我们平时所说的const迭代器就是const_iterator请注意区分与前者声明迭代器为const进行区分。 const与函数 const与函数参数 建议如果对于局部对象或对于函数参数没有改动的需求时建议将他们声明为const此时可以避免不必要的麻烦。比如将“”键值的“”。 const与函数返回值
函数返回一个常量往往可以降低因客户错误而造成的意外而且具有安全性和高效性 假设有这样的一个有理数类
class Rational;const Rational operator*(const Rational r1,const Rational r2)
{//...};Rational a,b,c;
...
(a*b) c;
由于是有理数类客户可能会将乘积再做一次赋值此时将operator*的返回值类型设置为const就可以避免这样无意义的赋值动作。 const与成员函数 const成员函数的好处 1. const成员函数易使class接口比较容易被理解清楚知道哪个函数可以改动对象内容而哪个函数不行。 2. 有了const成员函数const对象就能被“操作”因为const对象只能调用const成员函数能操作const对象就能利用传引用(const 类类型 )提高效率。 const成员函数与普通成员函数的区别
我们知道普通成员函数参数都有一个隐含的this指针是不能修改的也就是类 * const this;而const成员函数的this指针类型是const 类 * const this也就是说此时对象的内容也不能被修改。
class Date
{
public:
//... const char operator[](size_t position)const //operator[]for const对象{return _date[position];}char operator[](size_t position) //operator[]for non-const 对象{return _date[position];}private:string _date;};int main()
{Date d1(2024/08/04);cout d1[0] endl; //调用的是non-const版本const Date d2(2024/08/04)cout d2[0]; //调用的是const版本return 0;
} 说明
1. 两成员函数如果只是常量性不同也是可以被重载的。因为两个版本隐含的this类型不同同时
返回值类型也跟着不同。
2.const对象d2的对象指针为const Date* 更匹配const版本的operator[]因此调用const版本
而非const对象d1的对象指针为Date*,更匹配非const版本。 bitwise const VS logical const 到这里我们思考一下什么成员函数如果是const意味着什么目前有以下两个流行概念需要向大家介绍一下
bitwise constness 这种观点的人认为成员函数只有在不改变对象之任何成员变量时才可以说是const,也就是说不改变对象内的任何一个bit。这种论点的好处是很容易找到违反点只需找到对成员变量的赋值动作即可。 这种观点正是C对常量性的定义因此const成员函数不可以更改对象内任何非静态成员变量。 如果只有指针(而非所指向内容)隶属于对象(比如有这样的一个类将数据存储于char*而不是string)不修改char*而修改char*指向内容此时编译器是认为是bitwise constness的,可以正常通过编译。 class Block
{
public:Block( char* ch ):pText(ch){}char operator[](size_t position)const{return pText[position];}private:char* pText;
};int main()
{char arr[] hello;char* ch arr;const Block cctb(ch);char* pc cctb[0];*pc s;return 0;
}
此时可以通过这个漏洞修改成员变量指针指向的内容而不违法const
(注如果成员变量是string,由于operator[]需要访问pText成员变量并且你需要保证Block对象的状态不被修改所以pText的operator[]调用也必须是const的因此不会出现上述漏洞。)
这种情况导出所谓的logical constness.
logical constness
class BigArray
{vectorint v; int accessCounter;
public:int getItem(int index) const { accessCounter;return v[index];}
}; 此类提供了一个 getItem 接口除此之外为了计算外部访问数组的次数该类还设置了一个计数器 accessCounter 可以看到用户每次调用 getItem 接口accessCounter 就会自增很明显这里的成员 v 是核心成员而 accessCounter 是非核心成员。 我们希望接口 getItem 不会修改核心成员而不考虑非核心成员是否被修改此时 getItem 所具备的 const 特性就被称为 logic constness。
问题在这个函数中虽然accesCounter修改对对象而言可以被接受但是编译器只认bitwise constness不允许修改怎么办
mutable mutable是C中一个与const相关的摆动场他可以释放掉non-static成员变量的bitwise constness约束。 class BigArray {vectorint v; mutable int accessCounter; //像这样的成员变量可能总是会被更改。
public:int getItem(int index) const { accessCounter;return v[index];}
};
总结当const成员函数接受某些修改之后不改变成员函数逻辑状态的成员变量时这时可以使用mutable来释放const约束。但注意mutable可能会违反对象的不变性需要慎用。 在const和non-const成员函数中避免重复 对于“bitwise constness”非我所欲的问题mutable是个解决办法但并不能解决所有的难题。假设有个类类内的operator[ ]不单只是返回一个引用指向某字符也执行边界检验志记访问信息甚至可能进行数据完善性检验等...把所有这些放进const版本和非const版本的operator[ 里的问题是会导致代码膨胀以及大量代码重复
class Text
{public:char operator[](size_t pos){//... 边界检验//... 志记数据访问//... 检验数据完整性return text[pos];}const char operator[](size_t pos)const{//... 边界检验//... 志记数据访问//... 检验数据完整性return text[pos];}private:string text;};
避免代码重复的安全做法
class Text
{public:const char operator[](size_t pos){//... 边界检验//... 志记数据访问//... 检验数据完整性return text[pos];}char operator[](size_t pos)const{return (char)(((const Text )(*this))[pos]);}private:string text;};
说明
1. 这份代码进行了两次转型实现了了“运用const成员函数实现其non-const兄弟”避免了代码重
复。
2.第一次转型将(*this)也就是这个对象类型强转为const Text是为了匹配const版本调用const版
本的operator[ ]否则会陷入无限调用非const版本第二次转型则是用来从const operator[ ]的
返回值中移除const,这其中并未有权限放大的问题强转是可行的。
3. 反向调用也就是“令const版本调用non-const版本以避免代码重复”是一件错误的事因为non-
const并未承诺绝不改变其对象的逻辑状态因此这种做法可能使得对象被改动当然编译器也不
允许const调用非const也是一种权限的放大。 总结 1. 将某些东西声明为const可帮助编译器侦测出错误用法比如错误的赋值行为使得不必要的对象改动。 2.const可施加于任何作用域的对象,函数参数函数返回值成员函数。 3.如果在const成员函数内想改变非核心成员变量以达目的可利用mutable解除const约束。 4.当const与非const成员函数实质有着等价的实现且代码有大量重复时可考虑复用const版本以实现非const版本。