文章有点长哈,可以耐心的看看。个人总结,请持怀疑态度参考。
文章中代码的测试环境为 VS2013
进入正题。。。。
const关键字在C++中的用法多种多样,可以概括如下:
- 它可以在classes外部修饰global或namespace作用域中的常量或者修饰文件、函数或者块作用域中被声明为static的对象。
- 它也可以修饰classes内部的静态成员和非静态成员变量。
在修饰指针时首先要明确 const是修饰的指针自己(pointer),还是修饰的指针所指向的东西(data)。
贴一段代码
#include
using namespace std;
int main()
{
/*int num = 123;
int *p = #
*p = 222;
cout << num << endl; */
/*1. non-const pointer ; const data*/
/*int num = 123;
const int *p = #
// *p = 222; //报错
int num_s = 77;
p = &num_s;
cout <<*p<< endl;*/
/*2. const pointer ; non-const data*/
/*
int num = 123;
int * const p = #
*p = 222;
cout << num << endl;
int num_s = 555;
p = &num_s; //报错
*/
/*3. const pointer ; const data*/
/*int num = 123;
const int * const p = #
*p = 222; //报错
int num_s = 77;
p = &num_s; //报错
cout << num << endl;*/
getchar();
return 0;
}
可以用这段代码测试一下,代码中有注释如何修饰pointer和如何修饰data
总之:
如果const出现在左边 则指针指向的东西为常量,即修饰的是 data;如果const出现在 右边 则 指针本身为常量,不可修改,即修饰的是 pointer。
STL迭代器就像一个模拟的指针
- 如果你希望迭代器的指向不发生改变,即类似与 T*const 的作用,那么你可以这样写:const std::vector
:: iterator iter=a.end()
- 如果你希望迭代器指向的值不发生改变,那么应该用 const_iterator 。贴一段代码:
list<int> a;
a.push_back(1);
a.push_back(2);
/*const list::iterator iter = a.begin();
*iter = 5;
iter=a.end(); //报错 因为这个迭代器是常量 */
list<int>::const_iterator iter = a.begin();
*iter = 5; //报错 因为这个迭代器指向的值是常量
iter = a.end();
修饰成员函数的用法就不重复说了,可以看我的这篇博客点击这里
重点说一下 const成员函数的重要性 与 如何优化 同一个函数const 和 non-const 两个版本的代码(const ,non-const 是可以重载的 )
+——
1.const成员函数的重要性
//A.h class A { public: MyClass(){ }; ~MyClass(){}; int dis() const; int dis(); ..... private: ...... }; //main.cpp void print(const A & a) { a.dis(); //调用const版本 } int main() { .... }
必须重载dis() 并对不同版本给予不同的处理
+—-
2.如何突破const对成员函数的限制
在C++中,
mutable
可以突破const
的限制。被mutable
修饰的变量,将永远处于可变的状态,即使在一个const
函数中。为什么需要突破限制呢?
需要考虑到 某个函数 对客户(调用者)来说是不会修改任何数据的,但是实际上在实现这个函数是仍然有修改某些值的需要(只不过对客户来讲是不可见的,无法察觉的,也是没必要关心的)举个栗子:
你要写一个处理文本的类,让后类中需要有 返回文本字数的 成员函数。这个函数显然是不会修改客户的文本数据的,so 声明为const ,给客户承诺,我是不会改你的数据的 。但是实现代码时仍然有私有变量需要改动!!贴代码:
class Text
{
public:
Text(){ len_flag = false; };
~Text(){ };
size_t length() const
{
if (!len_flag)
{
text_len_new = strlen(text); //注意这两行
len_flag = true; // ...
}
return text_len_new;
}
private:
mutable size_t text_len_new; //最近一次计算的长度
mutable bool len_flag; //目前的长度是否有效
char *text;
};
int main()
{
Text t;
t.length(); //不会该数据,可以放心使用
}
+—–
3.在const 和 non-const中避免代码重复
(虽然相同的代码可以复制粘帖,但是代码重复太多显的很乱,而且不好维护!)
假如在上面的那个文本处理类中加一个成员 用来格式化显示文本,并且检查文本中是否有错误的内容贴代码
const char* dis_text() const
{
/*
一大块验证字符串合法和格式化等等代码
*/
return this->text;
}
char * dis_text()
{
/*
一大块验证字符串合法和格式化等等代码
*/
return this->text;
}
可以发现这两个版本的代码除了返回值不同,其他的都一样!!如何处理这么多的重复代码?
直接把相同的代码打包成一个函数,让后调用不就行了。其实这样做也可以,不过不是做好的做法,应函数调用需要有开支啊 。。解决方法 ,应用c++标准类型转换 ,一共有 4 种转换,可以自行百度一下 。用non-const的版本调用const的版本就可以了,有人可能不解,为什么要类型转换?HI 兄弟 你那么努力是在写BUG啊 !如果直接调用 是不会发生重载的,相当与没有结束条件的递归调用!!!!
注意:轻易别使用这种转换,玩不好就会危险。
贴代码:
const char* dis_text() const
{
/*
一大块验证字符串合法和格式化等等代码
*/
return this->text;
}
char * dis_text()
{
return const_cast<char*>(
//const_cast 把const修饰的给去掉
static_cast<const Text&>(*this).dis_text()
//把*this 转为 const 类型的对象 然后调用 dis_text()const 的版本
);
}
注意:不要调用反了,不要用const 调用non-const !!non-const可没有承诺说不改数据,如果反着调用const就很被动,鬼知道你在non-const中有没有改数据!
这一点也是很重要的 ,主要是针对返回 指针 引用!使用 const限制 可以避免很多安全隐患,甚至是你在写代码时不小心手抖写错的问题!
例如 类A 有重载 * 、 = 运算符 如果没有限制 在不经意间可能写出如下代码 :
A a(1); B b(2); if(a+b=5) //计划写 a+b==5 比较 { }
这种被隐式转换为bool类型的bug也不好找 。。。。。
我的个人网站 http://www.breeziness.cn/
我的CSDN http://blog.csdn.net/qq_33775402
转载请注明出处 小风code www.breeziness.cn