常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。
常量指针和指针常量
1. 何为顶层const和底层const
当const修饰指针的时候分为顶层const和底层const,声明指向常量的指针的const是底层const(指向常量的指针,不能通过该指针来改变所指的内容),声明常量指针的const是顶层const(常量指针,该指针是一个常量,指向地址不能改变)。
int a=1;
const int *p=&a; //该const是底层const,p为常量指针,不能通过*p来修改a的值
//*p=2; //错误,p指向的是常量
p++; //正确,可以改变p所指的地址
int *const q=&a; //该const是顶层const,q为指针常量,不能修改q指向的地址
*q=3; //正确,可以通过*q来改变a的值
//q++; //错误,不能改变q指向的地址
const int *const r=&a; //r为常量指针常量,第一个const是底层const,第二个const是顶层const
注意:指向“常量”的指针不代表它所指向的内容一定是常量,只是代表不能通过解引用符(操作符*)来改变它所指向的内容。例如上面可以直接通过a=2赋值改变a的值
2. 如何区分底层const和顶层const
const只对它左边的东西起作用,唯一的例外就是const本身就是最左边的修饰符,那么它才会对右边的东西起作用。const int *q=&a和int const *q=&a是一样的。即const在*右边时是顶层cosnt,此时指针是常量,const在*左边的时候是底层const,此时指针指向常量。
3. 区分底层const和顶层const的作用
- 执行对象拷贝时有限制,常量的底层const不能赋值给非底层的const。
int a=1;
const int *p=&a; //p为底层const的指针
//int *const q=p; //错误,不能将底层const指针赋值给非底层const指针
const int *r=p; //正确,可以将底层const指针赋值给底层const指针
int *const m=&a; //顶层const
const int *n=m; //正确,可以将顶层const赋值给底层const
- 使用命名的强制类型转换函数const_cast时,需要能够分辨底层const和顶层const,因为const_cast只能改变运算对象的底层const。
常量对象和常量成员函数
1. 常量对象
C++语言中,在定义某个类的对象时,若在整个说明语句前或者在对象名前面加一个关键字const就可以把它定义为常量对象。
<classname> const A
const <classname> A
C++不允许在常量对象上调用非常量成员函数,同时不允许声明常量的成员函数修改对象。
2. 常量成员函数
常量函数是C++对常量的一个扩展,它很好的确保了C++中类的封装性。在C++中,为了防止类的数据成员被非法访问,将类的成员函数分成了两类,一类是常量成员函数(观察者);另一类是非常量成员函数(变异者)。在一个函数的签名后面加上关键字const后该函数就成了常量函数。对于常量函数,最关键的不同是编译器不允许其修改类的数据成员。
class A
{
public:
A():i(0){ cout<<"A::A()"<<endl;}
~A(){ cout<<"A::~A()"<<endl;}
void read() const
{ cout<<"A::read(),now i="<<i<<endl;} //常量成员函数
void change()
{i=1; cout<<"A::change(),now i="<<i<<endl;} //非常量成员函数
private:
int i;
};
由于常量对象的状态不允许被修改,因此,通过常量对象调用非常量函数时将会产生语法错误。
A m; //非常量对象
m.read(); //正确,非常量对象可以调用常量函数
m.change(); //正确,非常量对象可以调用非常量函数
const A n=m; //常量对象
n.read(); //正确,常量对象只能调用常量函数,因为不希望修改对象状态
//n.change(); //错误,常量对象的状态不能被修改,而非常量函数存在修改对象状态的**可能**
每个成员函数都有一个隐含的指向对象本身的this指针。而常量函数则包含一个this的常量指针。例如对上面的函数:
void read(const A*this) const;
void change(A*this);
对于常量函数,我们不能通过this指针去修改对象对应的内存块。但是这仅仅是编译器的限制,我们仍然可以绕过编译器的限制,去改变对象的状态。例如将read()改为如下:
void read() const
{
A* ptr = (A*)this;
ptr->i=1;
cout<<"A::read(),now i="<<i<<endl;
}
如此就可以通过常量函数修改对象的成员。但是最好不要这样去使用。
3. 常量成员函数的重载问题
对于下面的类中的func函数
class A
{
private:
/*...*/
public:
void func() const;
void func();
}
对第一个func()函数,隐藏的形参为func(const A* this),而第二个func()函数形参为func(A * this),二者形参不同故构成了函数的重载,是允许的。存在同名同参数和返回值的常量函数和非常量函数时,具体调用哪个函数是根据调用对象是常量对像还是非常量对象来决定的。常量对象调用常量成员;非常量对象调用非常量的成员。
原文链接: https://www.cnblogs.com/chinono/p/13055337.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;
也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/353274
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!