公用数据的保护
- C++虽然采取了不少有效的措施(如设private保护)以增加数据的安全性,但是有些数据却往往是共享的,例如实参与形参,变量与引用,数据及其指针等,人们可以在不同的场合通过不同的途径访问同一个数据对象。有时在无意之中的误操作会改变有关数据的状况,而这是人们所不希望出现的。
- 既要使得数据能在一定范围内共享,又要保证它不被任意修改,这时可以使用const,即把有关的数据定义为常量。
常对象
- 可以在定义对象时加关键字const,指定对象为常对象。常对象必须要有初值,如:
Time const t1(12, 34, 46); //定义t1是常对象
。这样,在t1的生命周期中,对象t1中的所有数据成员的值都不能被修改。 - 定义常对象的一般形式为:
类名 const 对象名[(实参表)];
,也可以把const写在最左面:const 类名 对象名[(实参表)];
。二者等价。
在定义常对象时,必须同时对之初始化,之后不能再改变。
说明:
- 如果一个对象被声明为常对象,则通过该对象只能调用它的常成员函数,而不能调用该对象的普通成员函数(除了由系统调用的隐式的构造函数和析构函数)。常对象成员函数是常对象唯一的对外接口。
例如:(❌)
const Time t1(10, 15, 36); //定义常对象t1
t1.get_time(); //试图调用常对象t1中的普通成员函数,非法
引用常对象中的数据成员需将该成员函数声明为const即可。例如:void get_time(); //将函数声明为const
。这表明get_time是一个const型函数,即常成员函数
- 常成员函数可以访问常对象中的数据成员,但仍然不允许修改常对象中数据成员的值
- 有时在编程时有要求,一定要修改常对象成员中某个数据成员的值(例如一个类中有一个用于计数的变量count,其值应当能不断变化),C++考虑到实际编程的需要,对此作了特殊的处理,对该数据成员声明为mutable,如
mutable int count;
。把count声明为可变的数据成员,这样就可以用声明为const的成员函数来修改它的值
常对象成员
- 可以将对象的成员声明为const,包括常数据成员和常成员函数
常数据成员
- 其作用和用法与一般常变量相似,用关键字const来声明常数据成员。常数据成员的值是不能改变的。只能通过构造函数的参数初始化表来对常数据成员进行初始化,任何其他函数都不能对常数据成员赋值
- 如在类体中定义了常数据成员hour:
const int hour; //定义hour为常数据成员
,如果在类外定义构造函数,应写成以下形式:Time::Time(int h):hour(h){} //通过参数初始化表对常数据成员hour初始化
常成员函数
- 一般的成员函数可以引用本类中的非const数据成员,也可以修改它们。如果成员函数被声明为常成员函数,则只能引用本类中的数据成员,而不能修改它们
如:void get_time() const; //注意const的位置在函数名和括号之后
- 声明常成员函数的一般形式为:`类型名 函数名(参数表) const
- const为函数类型的一部分,在声明函数和定义函数时都要有const关键字,在调用时不必加const。常成员函数可以引用const数据成员,也可以引用非const的数据成员。const数据成员可以被const成员函数引用,也可以被非const的成员函数引用
怎样利用常成员函数呢?
- 如果在一个类中,有些数据成员的值允许改变,另一些数据成员的值不允许改变,则可以将一部分数据成员声明为const,以保证其值不被改变,可以用非const的成员函数引用这些数据成员的值,并修改非const数据成员的值
数据成员 | 非const成员函数 | const成员函数 |
---|---|---|
非const的普通数据成员 | 可以引用,也可以改变值 | 可以引用,但不可以改变值 |
const数据成员 | 可以引用,但不可以改变值 | 可以引用,但不可以改变值 |
const对象 | 不允许 | 可以引用,但不可以改变值 |
- 如果要求所有的数据成员的值都不允许改变,则可以将所有的数据成员声明为const,或将对象声明为const(常对象),然后用const成员函数引用数据成员,这样起到"双保险"的作用,切实保证了数据成员不被修改
- 如果已定义了一个常对象,只能调用其中的const成员函数,而不能调用非const成员函数(不论这些函数是否会修改对象中的数据)。这是为了保证数据的安全。如果需要访问对象中的数据成员,可将常对象中的所有成员函数都定义为const成员函数,并确保在函数中不修改对象中的数据成员
- 不要误认为常对象中的成员函数都是常成员函数。常对象只保证其数据成员是常数据成员,其值不被修改。如果在常对象中的成员函数未加const声明,编译系统把它作为非const成员函数处理。
- 还有一点要指出,常成员函数不能调用另一个非const成员函数。
指向对象的常指针
- 将指针变量声明为const型,这样的指针变量始终保持为初值,不能改变,即其指向不变
如:
Time t1(10, 12, 15), t2; //定义对象
Time* const ptr1; //const位置在指针变量名前面,指定ptr1是常指针变量
ptr1 = &t1; //ptrl指向对象t1,此后不能再改变指向
ptr1 = &t2; //错误,ptr1不能改变指向
- 定义指向对象的常指针变量的一般形式为:
类名 * const 指针变量名;
- 注意:指向对象的常指针变量的值不能改变,即始终指向同一个对象,但可以改变其所指向对象的值
- 如果想将一个指针变量固定地与一个对象相联系(即该指针变量始终指向一个对象),可以将它指定为const型指针变量。这样可以防止误操作,增加安全性
- 往往常指针作为函数的形参,目的是不允许在函数执行过程中改变指针变量的值,使其始终指向原来的对象。如果在函数执行过程中修改了该形参的值,编译系统就会发现错误,给出出错信息,这样比人工来保证形参值不被修改更可靠
指向常对象的指针变量
- 定义指向常变量地指针变量的一般形式为:
const 类型名 * 指针变量名;
说明
- 如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向它,而不能用一般的(指向非const型变量的)指针变量指向它
对象的常引用
- 一个变量的引用就是变量的别名。实际上,引用是一个指针常量,用来存放该变量的地址。如果形参为变量的引用,实参为变量名,则在调用函数进行虚实结合时,把实参变量的地址传给形参(引用),这样引用就指向实参变量。对象的引用也是与此类似的。也可以把引用声明为const,即常引用。
例:对象的引用和常引用
#include<iostream>
using namespace std;
class Time
{
public:
Time(int, int, int);
int hour;
int minute;
int sec;
};
Time::Time(int h, int m, int s) //定义构造函数
{
hour = h;
minute = m;
sec = s;
}
void fun(Time& t) //形参t是Time类对象的引用
{
t.hour = 18;
}
int main()
{
Time t1(10, 13, 56); //t1是Time类对象
fun(t1); //实参是Time类对象,可以通过引用来修改实参t1的值
cout << t1.hour << endl; //输出t1.hour的值为18
return 0;
}
如果不希望在函数中修改实参t1的值,可以把fun函数的形参t声明为const(常引用),函数fun的首行为void fun(const Time &t);
。这样在函数中就不能改变t所代表的实参t1的值。注意:不是不能改变"引用t"的指向,而是t所指向的变量的值。
- 在C++面向对象程序设计中,经常用常指针和常引用作函数参数。注意既能保证数据安全,使数据不能被随意修改,在调用函数时又不必建立实参的拷贝。
const型数据的小结
形式 | 含义 |
---|---|
Time const t; | t是常量,其值在任何情况下都不能改变 |
void Time::fun() const | fun是Time类中的常成员函数,可以引用,但不能修改本类中的数据成员 |
Time * const p; | p是指向Time类对象的常指针变量,p的值(p的指向)不能改变 |
const Time * p; | p是指向Time类对象的指针变量,p指向的类对象的值不能通过p来改变 |
const Time &t1=t; | t1是Time类对象t的引用,二者指向同一存储空间,t的值不能改变 |