色戒被删,valueof,华夏红利前
目录
关键字const可以用于以下对象。
stl迭代器以指针为根据塑模出来,所以stl迭代器的作用就像个t *指针。
std::vector<int> vec; const std::vector<int>::iterator iter = vec.begin(); //iter相当于t *const指针 *iter = 10; //没问题 ++iter; //错误,iter不可修改 std::vector<int>::const_iterator const_iter = vec.begin(); //const_iter相当于const t *指针 *const_iter = 10; //错误,*const_iter不可修改 ++const_iter; //没问题
const最具威力的用法是面对函数声明时的应用。在一个函数声明中,const可以和函数参数、函数返回值、函数自身(如果是成员函数)产生关联。
const用于函数参数只需记住一条原则:除非函数体中需要改动参数,否则就将它们声明为const。
const用于函数返回值,往往可以降低因使用错误而造成的意外,同时又不至于放弃安全性和高效性,举个例子,看下面有理数类operator *的声明。
class rational { ... }; const rational operator * (const rational &lhs, const rational &rhs);
rational a, b, c; (a * b) = c; //有意错误,对两个数的乘积进行赋值,就好比1 = 2一样 if (a * b = c) //无意错误,将==漏写为=
将const用于成员函数的目的,是为了确认该成员函数可作用于const对象身上。const成员函数之所以重要,基于两个理由。
第2条对于编写高效代码是个关键,因为如条款20所述,改善c++程序效率的一个根本方法是以const引用的方式传递对象。
const引用的可能是const对象,而const对象只能调用const成员函数。
所以此技术可行的前提是,有const成员函数可用来处理取得的const对象;否则,就算能将const对象传进来,也没有办法去处理它。
c++有一个重要特性:两个成员函数如果只是常量性不同,则可以构成重载,即使它们的参数类型、参数个数、参数顺序都完全一致。
基于这个特性,再结合上面提到的高效编码技巧,就可以得出如下所示的接口设计。
class textblock { private: std::string text; public: //用于const对象,由于const对象内容不允许修改,因此返回值也加了const const char &operator [] (std::size_t postion) const { return text[postion]; } //用于non-const对象 char &operator [] (std::size_t postion) { return text[postion]; } }; void print(const textblock &text) { std::cout << text[0]; } textblock text; const textblock const_text; print(text); //调用char &operator [] () print(const_text); //调用const char &operator [] () const
c++编译器要求const成员函数不能更改对象内的任何non-static成员变量,简单地说就是const成员函数中不能出现对non-static成员变量的赋值操作。
这种要求实质上是不能更改对象内的任何一个bit,因此叫做bitwise constness。
不幸的是,许多const成员函数虽然不完全具备const性质,但却能通过c++编译器的bitwise检验,更具体地说,就是:
class textblock { private: char *ptext; public: char &operator [] (std::size_t postion) const { return ptext[postion]; } }; const textblock text("hello"); //声明一个const对象 char *pc = &text[0]; //调用const char &operator []取得一个指针,指向text的数据 *pc = 'j'; //通过pc指针将text的数据改为了"jello"
上面这个class将operator []声明为const成员函数,但却返回了一个reference指向对象内部数据,这种做法是错误的,条款28对此有深刻讨论,我们暂时先忽略它。
从编译器bitwise constness的角度看,上述代码不存在任何问题,但你终究还是改变了const对象的值,这种情况导出所谓的logical constness。
logical constness指的是,const成员函数可以修改它所处理对象内的某些bits,但前提是用户察觉不到这种修改。
要想在const成员函数中修改non-static成员变量,需要对这些成员变量使用mutable
关键字,mutable可以去除non-static成员变量的bitwise constness约束。
class ctextblock { private: char *ptext; mutable std::size_t textlength; //最近一次计算的文本长度 mutable bool lengthisvalid; //目前的长度是否有效 public: std::size_t length() const; }; std::size_t ctextblock::length() const { if (!lengthisvalid) { textlength = std::strlen(ptext); lengthisvalid = true; } return textlength; }
length()的实现当然不是bitwise constness,因为textlength和lengthisvalid都可能被修改,但这两个成员变量被修改对于const ctextblock对象是可以接受的。
现在我们对class textblock做一些修改,假设operator []不单只是返回一个reference指向某字符,还执行边界检查、日志数据访问、数据完整性检验等工作。
class textblock { private: std::string text; public: //用于const对象,由于const对象内容不允许修改,因此返回值也加了const const char &operator [] (std::size_t postion) const { ... //边界检查 ... //日志数据访问 ... //数据完整性检验 return text[postion]; } //用于non-const对象 char &operator [] (std::size_t postion) { ... //边界检查 ... //日志数据访问 ... //数据完整性检验 return text[postion]; } };
operator[]的const和non-const版本中的代码重复,可能会随着编译时间、持续维护、代码膨胀等因素而成为令人头痛的问题。
将重复代码封装到一个private函数中,并分别在两个函数中调用它,不失为一个解决该问题的好办法,但依然存在代码重复,如函数调用、return语句。
真正最好的办法是:先实现operator []的const版本,然后在non-const版本中调用它。如下示例代码所示,这种方法有两个技术要点。
class textblock { private: std::string text; public: //用于const对象,由于const对象内容不允许修改,因此返回值也加了const const char &operator [] (std::size_t postion) const { ... //边界检查 ... //日志数据访问 ... //数据完整性检验 return text[postion]; } //用于non-const对象 char &operator [] (std::size_t postion) { const textblock &const_this = static_cast<const textblock &>(*this); //将自身从textblock &转换为const textblock & return const_cast<char &>(const_this[postion]); //调用const版本的operator [],并去除返回值中的const属性,然后返回 } };
注意,千万不要令const版本调用ono-const版本来避免代码重复,因为const版本调用non-const版本的唯一方法是去除自身的const属性,这绝对不是个好事情。
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
如何在没有core文件的情况下用dmesg+addr2line定位段错误
用QT制作3D点云显示器——QtDataVisualization
网友评论