0%

const的使用

对于const,最为熟悉的就是,它允许你指定一个语义约束,也就是指定一个不该被改动的对象,而编译器会强制实施这项约束。const 修饰的值是只读的变量,而不是常量,其值在编译时不能被使用,因为编译器在编译时不知道其存储的内容。下面程序说明

1
2
3
4
5
6
7
8
9
// .c file , c project
#include <stdio.h>
const int Size = 100;

int main()
{
int arr[Size]; //编译出错
return 0;
}

C语言中,编译出错,表明 const 修饰的值不是常量(C语言)。但在C++中同样的代码却不会出现编译错误,因为C++扩展了const 的含义,所以C++和C在某些细节上还是有区别的,不过const在C++中更多的是应用在类中,这里主要讨论C++中的const 。

含指针情况下,const 到底修饰谁?先忽略类型名,const 离谁近,就修饰谁。或者,如果关键字 const 出现在 * 左边,表示被指的对象不可改;如果出现在 * 右边,则表示指针自身不可改,如果两边都有,那就是指针和指针所指的对象不可改。

下面就重点探讨C++类中的 const。

一、const 数据成员

const 数据成员只在某个对象生存期内是只读变量,而对于整个类而言是可变的,因为类可以创建多个对象,不同的对象其 const 数据成员可以是不同值,const 数据成员必须被初始化,且必须使用初始化列表,不能在构造函数的函数体中初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*错误初始化方式*/
class Test
{
public:
Test()
{
i = 5; //错误
}
private:
const int i;
};

/*正确初始化方式*/

class Test
{
public:
Test():i(5) {}
private:
const int i;
};

因为定义是在类本身定义的时候进行的(class {}范围),调用构造函数的时候,首先是给该实例(类的具体对象)分配空间,使用初始化列表就是在分配空间的时候,同时将其空间初始化,而在构造函数的函数体内,所有变量(常量)的空间都已经分配好了,对于const成员变量本身是不能改变其值的,构造函数的函数体{}内的操作只能是赋值,对const 成员变量赋值操作,自然是非法操作。所以const 数据成员的初始化只能在类构造函数的初始化表中进行。

二、const 函数

重头戏来了,const 最具威力的用法是面对函数声明时的应用,在一个函数声明式内,const 可以和函数返回值、各参数、函数本身(成员函数)产生关联。

1、const 函数返回值,即令函数返回一个const 类型,最突出的应用就是运算符重载时保证与内置类型运算符兼容,这样在某些运算符重载时,就不会自创出一些没意义的自定义类型运算符。

1
2
3
class Rational { ... };

const Rational operator* (const Rational& lhs, const Rational& rhs);

这样当指向下列操作时就会编译出错。

1
2
3
Rational a, b, c;

(a * b) = c; //error

如果函数返回值不为const 类型,那么上面的操作是不会出错的,但是这与内置类型不兼容,内置类型不允许对两个数值的乘积再做一次赋值。只要const 了函数返回至,那么这个函数调用就别想作左值。

2、const 参数,保证函数内部不会修改该值,const 作为形参自然伴随着引用或指针,不然就没有实际意义,常用的是 const 引用形参,这样既确保了被调函数不会改变主调函数的数据,又不会像非指针、非引用的参数那样要发生复制,提高效率,虽然传值形式的函数调用也不会修改主调函数的数据,但函数调用时需要额外开辟栈空间拷贝一份副本。需要注意的是形参为const 时,最好实参也为const。如果主函数里面定义了一个const 变量,那么操作该变量的函数的对应形参就必须是const。

3、const 成员函数,将const 实施于成员函数的目的,是为了确认该成员函数可作用于const 对象身上。成员函数声明为const 类型,就意味着该函数不允许修改类中的成员数据(除非成员数据标记为 mutable),这样编译器看到函数定义为const ,就知道并认为该函数不会去修改类成员数据。

const 成员函数的定义(声明)方式怪怪的,const 放在函数名后面,函数体前面

1
2
3
4
5
6
7
8
class Test
{
public:
int fun(int random_arg) const
{
//code
}
};

实际上上面的函数可理解为以下形式

1
int fun(const Test* this, int random_arg) const {	/*code*/  }

修饰符const 的作用是限制指针this 指向的对象(const 在 * 左边)只读,也就是调用该函数的对象是只读,该函数不可修改该对象,一旦发现函数体内有修改该对象数据的行为就报错。C++中,this指针是隐藏的,所以就有了前面的const 成员函数声明方式。

C++规定,const 对象只能与const 成员函数连用,目的是为了保护const 对象数据不被改写。另外const成员函数的声明与定义形式需保持一致性,否则编译器将不认为二者是同一个函数。

总结:

1)const成员函数可以访问非const对象的非const数据成员、const数据成员,也可以访问const对象内的所有数据成员;

2)非const成员函数可以访问非const对象的非const数据成员、const数据成员,但不可以访问const对象的任意数据成员;

3)作为一种良好的编程风格,在声明一个成员函数时,若该成员函数并不对数据成员进行修改操作,应心可能将该成员函数声明为const 成员函数。