const

const与指针及函数

cppreference-zh-201806 中的简单定义

const 对象——类型为 const-qualified 的对象,或 const 对象的非 mutable 子对象。这种对象不能修改:尝试直接这么做是编译时错误,且尝试间接这么做(例如通过到非 const 类型的引用或指针修改 const 对象)导致未定义行为。

const与指针

声明指针时,在类型前或后都可以使用关键字const,也可在两个位置都使用。例如,下面都是合法的声明,但是含义大不同:

1
2
3
const int * pOne;    //指向整形常量 的指针,它指向的值不能修改
int * const pTwo; //指向整形的常量指针 ,它不能在指向别的变量,但指向(变量)的值可以修改。
const int *const pThree; //指向整形常量 的常量指针 。它既不能再指向别的常量,指向的值也不能修改。

const与函数

《代码大全》说:代码是写给人看到的,我觉得const的设计就出于这个目的。即,通过避免人为的失误来提升程序的健壮性。从这个角度出发,很多设计的合理性就显而易见了。

用const修饰函数的参数

应用背景

我们经常会将自定义类型作为函数参数。如果使用值传递,则会产生临时对象。临时对象的构造、复制、析构过程都将消耗资源。因此,我们会使用引用传递来避免这个开销。引用传递意味着我们可以修改实参的值,我们有时候并不希望这件事发生,需要限制函数体对实参的修改的能力。

应用方法

在引用传递的基础上加上const关键词,即:const A &a。这个语法中兼备了高效和安全。其中,&保证了高效,const缩小了权限,从而保证了安全。

错误的应用

  • void f(const int a) 值传递本身就意味着对形参的修改不会影响实参。写法并非错误,事实上在某些场合也具备意义。但是对于避免影响形参这个目的,是无意义的。
  • void f(const int &a) 像int这样的内部类型作为函数参数被传递时,不存在构造、复制、析构的过程,复制的速度非常快。这种写法和void f(int a)的效率几乎相当。写法并非错误,事实上在某些场合也具备意义。但是对于安全又高效地使用实参这个目的,是无意义的。

    用const修饰函数的返回值

    申明格式为const A* GetA(void);。和上一节同理,也是为了更安全地使用&。引用传递返回值一般出现在链式表达中,const则限制了不正常的链式表达。

程序举例:

1
2
3
4
5
6
7
8
9
10
11
12
class A
{
A& operate = (const A &other);// 赋值函数
};

int main(){
A a, b, c; // a, b, c 为A的对象
a= b = c; // 正常的链式赋值
(a= b) = c; // 不正常的链式赋值,但合法。如果将赋值函数的返回值加const修饰,那么该返回值的内容不允许被改动。语句a= b = c 仍然正确,但是语句(a= b) = c 则是非法的。

return 0;
}

用const修饰成员函数

申明格式举例:int C::f(void) const;。作用是:f函数不可以修改数据成员,或者调用其它非const成员函数。

程序举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Stack
{
public:
void Push(int elem);
int Pop(void);
int GetCount(void) const; // const 成员函数
private:
int m_num;
int m_data[100];
};
int Stack::GetCount(void)const
{
++ m_num; // 编译错误,企图修改数据成员m_num
Pop();// 编译错误,企图调用非const函数
return m_num;
}

为什么const放在后面?因为const在修饰成员函数时,其实修饰的是*this。而this没有显式地写出来,所以就放在后面了(个人理解)。

如果调用了非const成员函数,VS2017会报类似于这样的错误:

error C2662: “CDate::COMPARE CDate::comparef(CDate,const CDate &)”: 不能将“this”指针从“const CDate”转换为“CDate &”

注意:const不能修饰静态成员函数。而静态成员函数属于类而非对象,因此不可以被const修饰。

参考来源