一起读《Effective C++》: 条款03:尽可能使用const
条款03:尽可能使用const
Use const whenever possible.
1. 定义一个常量指针?指针常量?
条款03 上来直接整一个比较容易记混 的 知识点,使用const来定义 指针和指针所指的变量 为常量:
char greeting[] ="Hello";
char* p= greeting;
const char* p = greeting;
char* const p = greeting;
const char* const p = greeting;
记忆的话只要考虑const 和* 的先后问题就行了, 先有const ,那就是实际指向的东西为常量; 先有* ,那就是指针是常量。
定义的时候 int等类型关键字 的位置倒是没有影响:
void f1(const A* a);
void f2(A const* b);
2. STL迭代器与const
STL的迭代器本身就是以指针为范本构造出来的,所以迭代器与const一起使用的时候语法类似指针和const:
const std::vector<int>::interator iter = vec.begin();
std::vector<int>::const_interator cIter= vec.begin();
3. 函数返回值与const
让函数的返回值设置成const一般用在操作符重载函数里面:
class Rational {...};
const Rational operator* (const Rational& lhs, const Rational& rhs);
主要是为了防止以下这种情况发生:
Rational a,b,c;
(a*b) =c;
这种操作有可能是写代码的时候手抖了,这个时候就需要用const来提示你这个写法不对。
4. 成员函数与const
成员函数是const 意味着什么? 是指我成员函数在执行的过程中不改变所在对象的任何一个bit(bit constness )嘛? 还是说这个const成员函数可以修改他所处理的对象内部的一些bits,但是最终并没有改变实质性的内容,客户端也检测不出来这些改变(lobical constness )呢?
bit constness
下面这个例子里面我们的成员函数为const 类型,不会改变类中任何一个bit,但是却有可能使得类里的变量在类外面被轻易的改变。(现在这种办法在C++中已经可以被检测出来并且编译报错)
class Text(){
public:
Text(const char* c) {ptr=const_cast<char*>(c);}
char& operator[](size_t position) const{return ptr[position];}
void print() const{
cout<<ptr;
}
private:
char* ptr;
}
int main(){
const Text txt("Text");
char* char0 = &txt[0];
*char0='J';
txt.print();
return 0;
}
(注释掉*char0=‘J’;之后就可以正常输出)
这种办法已经可以检测到了,所以大家大概了解就行。
logical constness
修改类为以下代码会报错:error: assignment of member 'Text::textLength' in read-only object ,是因为length的时下不是bitwise const ,因为textLength 和 lengthIsValid 都可能被修改,事实上这他俩代表的是数据的长度和有效性本来就应该被修改,那这时候咋办?我们引入顶级手法2 :使用mutable 释放non-static 成员变量的bitwise constness约束。
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
class Text{
public:
Text(const char* c) {ptr=const_cast<char*>(c);}
char& operator[](std::size_t position) const{
return ptr[position];
}
void print() const{
cout<<ptr;
}
size_t length() const;
private:
char* ptr;
size_t textLength;
bool lengthIsValid;
};
size_t Text::length() const{
if(!lengthIsValid){
textLength=strlen(ptr);
lengthIsValid=true;
}
return textLength;
}
int main(){
const Text txt("Text");
char* char0 = &txt[0];
*char0='J';
txt.print();
return 0;
}
这个部分会报错: 顶级手法2 :给 tesxtLength和lengthIsValid变量加上mutable 前缀就OK了
5. const 和non-const 成员函数的重复问题
mutable 也不能解决全部的const难题,诸如下面这个例子中的重复问题:
class Text{
public:
const char& operator[](size_t position) const {... return text[position];}
char& operator[](size_t position) {... return text[position];}
};
代码重复会带来的编译时间、维护、代码膨胀的问题(实际上这种代码重复非常常见,就算是一些开源工具的源码都是如此),这个时候我们可以使用cast转型来优化,在non-const里面使用const的函数,并且 使用const_cast 来减少代码重复:
class Text{
public:
const char& operator[](size_t position) const {...}
char& operator[](size_t position) {
return const_cast<char&>(static_cast<const Text>(*this)[position]);
}
};
这个手法中的 static_cast(*this) 作用是把Text转为const const_cast<char&>作用是把返回值的const移除。
|