const 这个关键字会将所修饰的表达式置为常量,编译器会尽全力保证该表达式不可被修改,在const不和指针或者引用结合是,数据的类型是无关const的,但是如果一但和指针和引用相结合,那么考虑数据类型时候必须要考虑const了
class Test
{
public:
Test(int data = 10):_ptr(new int(data)){}
~Test(){
delete _ptr;
}
int GetInt(){
return *_ptr;
}
int GetInt()const{
return *_ptr;
}
int& GetIntRef(){
return *_ptr;
}
int& GetIntRef()const{
return *_ptr;
}
int* GetIntPtr(){
return _ptr;
}
int* GetIntPtr()const{
return _ptr;
} // int *const _ptr
int*& GetIntPtrRef(){
return _ptr;
}
int*const& GetIntPtrRef()const{
return _ptr;
}
private:
int *_ptr;
};
int main()
{
Test t1;
const Test t2;
int a1 = t1.GetInt();
int &b1 = t1.GetInt();
const int &c1 = t1.GetInt();
int a2 = t2.GetInt();
int &b2 = t2.GetInt();
const int &c2 = t2.GetInt();
return 0;
}
举个例子,上面代码种有几处是有错误的,找出它~~~~
上面代码种b1是错误的,b2也是
先来看看b1,getInt返回的是一个整形值,它所返回的是一个立即数,那么用引用接收的时候就会出现无法给立即数取址的问题,此时寄存器带出的是一个立即数,而立即数不会有地址的,所以如果要引用,必须是一个常引用,否则编译不通过。
b2也是同样的,立即数不能取址
那如果一开始返回的不是立即数而是一个引用呢?
int main()
{
Test t1;
const Test t2;
int a1 = t1.GetIntRef();
int &b1 = t1.GetIntRef();
const int &c1 = t1.GetIntRef();
int a2 = t2.GetIntRef();
int &b2 = t2.GetIntRef();
const int &c2 = t2.GetIntRef();
return 0;
}
这样编译下来,并没有什么问题,原因是返回一个引用,此时会生成一个临时量,不存在立即数的问题了
那么我们再来看看如果要是返回一个指针呢?
int main()
{
Test t1;
const Test t2;
int *a1 = t1.GetIntPtr();
int *&b1 = t1.GetIntPtr();
const int *&c1 = t1.GetIntPtr();
int *a2 = t2.GetIntPtr();
int *&b2 = t2.GetIntPtr();
const int *&c2 = t2.GetIntPtr();
return 0;
}
此时又出现了刚才的问题,指针本身也是一个在32位系统下4字节的基础类型,所以返回值也会是一个立即数,不能取址,必须用常引用
简单总结一下:
对于赋值表达式的两边
- 如果是值,只需要类型相同
- 如果是指针,要求必须能取地址
- 如果是引用,必须是能取地址,类型相同
再补充3个例子
int*& GetIntPtrRef(){
return _ptr;
}
int*const& GetIntPtrRef()const{
return _ptr;
}
int main()
{
Test t1;
const Test t2;
int *a1 = t1.GetIntPtrRef();
int *&b1 = t1.GetIntPtrRef();
const int *&c1 = t1.GetIntPtrRef();
int *a2 = t2.GetIntPtrRef();
int * &b2 = t2.GetIntPtrRef();
const int *&c2 = t2.GetIntPtrRef();
return 0;
}
此时编译下来有3个错误,本质上还是类型不相同导致的,GetIntPtrRef()const的返回值是int const,但是b2是一个int *&,所以需要再&前面加上const保持类型相同
c2也是一样的道理,需要再引用前面加const
这里还有一个c1的错误,这个错误有些特殊,前面说过,const修饰的变量,编译器会尽力保证这个值不可被修改,此时const修饰的是&c1这个表达式,所以通过c1是不能修改的,但是此时却可以通过c1这个引用变量修改c1的内存,将引用换个指针就更加明显了,q,p这两个变量都可以修改同一片内存,此时我们告诉编译器,q这是个常量,不准修改了,但是编译器发现通过*p居然也可以修改这里的内存,必然是通过不了的。修改的话可以限定常引用,防止修改同一片内存
const int *const &c1 = t1.GetIntPtrRef();
再看第二个
int ** GetIntPtrPtr()
{
return &_ptr;
}
int *const* GetIntPtrPtr()const{
return &_ptr;
}
int main()
{
Test t1;
const Test t2;
int **a1 = t1.GetIntPtrPtr();
int **b1 = t1.GetIntPtrPtr();
const int **c1 = t1.GetIntPtrPtr();
int **a2 = t2.GetIntPtrPtr();
int **b2 = t2.GetIntPtrPtr();
const int **c2 = t2.GetIntPtrPtr();
return 0;
}
此时有4个错误,再常对象t2中_ptr是一个常量不可被修改的,所以 t2所有接收的类型都是int const ,故而a2,b2,c2都有错误
而c1中也就是那个特殊的错误,const修饰的变量编译器会尽力防止内存修改,所以应该是const int const 类型,故而c1也有错误
再来看看最后一个例子
int ** const& GetIntPtrPtrRef(){
return &_ptr;
}
int *const * const& GetIntPtrPtrRef()const{
return &_ptr;
}
int main()
{
Test t1;
const Test t2;
int **a1 = t1.GetIntPtrPtrRef();
int **b1 = t1.GetIntPtrPtrRef();
const int **c1 = t1.GetIntPtrPtrRef();
int **a2 = t2.GetIntPtrPtrRef();
int **b2 = t2.GetIntPtrPtrRef();
const int **c2 = t2.GetIntPtrPtrRef();
return 0;
}
这个的错误和上个例子十分相似,在此不多赘述了。
总结:
- 等式的两边先进行类型的比较,需要注意const在右边没有*时候是不计算类型的,保证两边的类型首先相同。
- 其次注意const修饰的变量,指针或者引用是必须要求参数能够取地址的,如果是一个立即数的话,就必须注意要用常引用。
- 再就是那个特殊的错误,对于const与二级指针的结合,要求const修饰的对象的那块内存不能修改,所以两个*之前要么都有const,要么都没有。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。