const作用:const允许你指定一个语义约束(也就是指定一个“不该被改动的对象”),而编译器会强制实施这项约束,它允许你告诉编译器和其它程序员其值应保持不变。
1、常指针、指针常量
- 常指针:const pointer, non-const data(不允许修改指针指向,允许修改指针所指向的数据)
- 指针常量:non-const pointer, const data(允许修改指针指向,不允许修改指针所指向的数据)
char greeting[] = "Hello";
char* p = greeting; //non-const pointer,non-const data
const char* p = greeting; //non-const pointer,const data
char* const p = greeting; //const pointer,non-const data
const char* const p = greeting; //const pointer,const data
区分技巧:如果关键字
const在*号左边,表示被指物是常量(指针常量);如果关键字const在*号右边,表示指针是常量(常指针);如果*号两边都有关键字const,表示被指物和指针都是常量;const Widget* pw; 等效于 Widget const * pw; const在*号左边,都是指针常量
2、函数返回值类型、函数参数
- 函数返回值类型:
const int add(int a, int b); - 函数参数:
int add(const int& a, const int& b);
函数返回值类型:主要避免“想要键入==,却意外键入=”的错误,例如:
if( add(a,b) == c ) 却打成了 if( add(a,b) = c )
函数参数:主要避免函数主体修改实参的值;用const修饰函数参数,一般都是pass-by-reference,因为如果是pass-by-value,函数内的实参只是实参的一个副本,并未起到const的作用,而且pass-by-value需要拷贝副本,效率低下。
3、const成员函数
- non-const函数和const函数是可以重载的;
- non-const对象调用non-const函数,const对象调用const函数;
class Widget
{
public:
...
int add(const int& a, const int& b)
{
return a+b;
}
int add(const int& a, const int& b) const
{
return a*b;
}
}
Widget w1;
const Widget w2;
qDebug()<<w1.add(1,2); //输出3,调用1+2
qDebug()<<w2.add(1,2); //输出2,调用1*2
4、const、mutable
bitwise const阵营的人认为:成员函数只有在不修改对象的任何成员变量(static除外)时才可以说是const;
这也是有问题的,例如:
class Widget
{
public:
...
char& func_s(const char& s1) const;
char* s;
}
const Widget w;
w.s = func_s("Hello");
w.s[0] = 'L';
const成员函数func_s确实不会改变任何成员变量,可以通过编译,但是它返回了char&,最后只对它调用const成员函数,还是修改了它的值;
这就引出了logical constness,他们主张:一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才如此。
如果编译器检查bitwise constness,那就可以使用mutable,释放掉non-static成员变量的bitwise constness约束:
class Widget
{
public:
...
char& func_s(const char& s1) const
{
strcpy(s,s1);
}
mutable char* s;
}
5、在const和non-const成员函数中避免重复
class Widget
{
public:
...
const char& func_s(const char& s1) const
{
... //1、边界检测
... //2、数据访问
... //3、检验数据完整性
return xxx;
}
char& func_s(const char& s1)
{
... //1、边界检测
... //2、数据访问
... //3、检验数据完整性
return xxx;
}
}
上例中重载了const成员函数和non-const成员函数,但是它们中有相同功能的代码块,当然也可以将重复部分提取出一个函数,然后在其中调用。
令non-const成员函数调用const成员函数是一个避免代码重复的安全做法(即使过程中需要一个转型动作),例如:
class Widget
{
public:
...
const char& func_s(const char& s1) const
{
... //1、边界检测
... //2、数据访问
... //3、检验数据完整性
return xxx;
}
char& func_s(const char& s1)
{
//1、*this获取调用对象
//2、然后通过static_cast转换成const对象,去调用const成员函数
//3、最后通过cosnt_cast消除const(因为自身是non-const成员函数),并返回
return const_cast<char&>(
static_cast<const Widget&>(*this)
(func_s("Hello"))
);
}
}
但是反向做法,令const成员函数调用non-const成员函数却是一种错误行为
因为const成员函数承诺绝不改变其对象的逻辑状态,non-const成员函数却没有这般承诺。如果在const函数中调用non-const函数,就有可能修改对象的逻辑状态
总结
- 将某些东西声明为const可帮助编译器侦测处错误用法;
- const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体;
- 编译器强制实施bitwise constness,可使用mutable,使用“逻辑上的常量性”;
- 当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复;
本文深入探讨了C++中const的多种用法,包括常指针与指针常量、函数返回值类型和参数、const成员函数以及const与mutable的结合使用。强调const用于增加代码的逻辑清晰性和编译时错误检查的重要性,同时提供了解决const成员函数与non-const成员函数代码重复问题的策略。
1063

被折叠的 条评论
为什么被折叠?



