一、指针与const
- 容易混淆的几个const指针
const int p; //常变量 const int* p; //数据是不可变,指针可变 int const* p; //数据是不可变,指针可变 int * const p; //指针是不可变,数据可变 const int * const p; //指针是不可变,数据不可变 int const * const p; //指针是不可变,数据不可变
二、迭代器与const
- 迭代器常量:迭代器在功能上相当于指向某类型T的指针 T*,使用const iterator相当于定义一个迭代器为一个常量(T* const)
const std::vector<int>::iterator it = v.begin(); //注意,此声明只表示迭代器本身是常量 *it = 10; //编译通过,迭代器是常量,但数据可以被修改 ++it; //编译失败!因为const迭代器不允许被改变!
- 迭代器内容常量:使用const_iterator
std::vector<int>::const_iterator it = v.begin(); //使用了const_iterator类型 *it = 10; //编译失败,数据不允许被改变! ++it; //编译通过,迭代器本身可以被改变
三、返回const
- 尽量使用const可以帮助调试
注:编译器在此并不会报错,因为只有当a,b,c是C++自有类型(比如int)才会报错,对于用户自定义的类,编译器会认为此操作是将一个Rational赋值给另一个Rationalclass Rational{....}; Rational operator*(const Rational& lhs, const Rational& rhs){...} //进行如下操作 Rational a,b,c; if(a*b = c){......}
方法:使用将该操作符定义为返回const,这样对其赋值将会是非法操作const Rational operator*(const Rational& lhs, const Rational& rhs){...}
四、成员函数与const
- 作用:
a、它可以让接口更加直观,直接告诉用户这个函数是不是只读(Read only),会不会改变某变量;
b、用const修饰的对象只能调用const修饰的成员函数,因为不被const修饰的成员函数可能会修改其他成员数据,打破const关键字的限制;Text t("Hello"); const Text ct("Hello"); std::cout<<t[0]; //调用了不加const修饰的索引操作符 std::cout<<ct[0]; //调用了const版本, 但如果只有不加const的操作符,将会报错discard qualifier t[0] = 'x'; //成立,但注意此索引操作符必须声明为引用才可以支持赋值操作 ct[0] = 'x'; //错误!常量不能被修改
五、const的数据常量性和逻辑常量性
数据常量性
:不允许常量对象的成员数据被修改(但只检查该成员函数有没有给成员数据进行赋值操作),但可以修改了某个数据,这样照样可以通过编译器的检测;const Text ct("Hello"); //构造某常量对象 char* pc = &ct[0]; //取其指针 *pc = 'K'; //通过指针修改常量对象,编译不会报错,结果为"Kello"
逻辑常量性
:允许某些数据被修改,只要这些改动不会反映在外,用mutable关键字来解决;除mutable之外,静态成员(static)也可以被const成员函数修改;mutable std::size_t length; mutable bool lengthValid;