常对象的使用

2015/05/28 C和C++基础

C++虽然采取了不少有效的措施(如设private进行保护)以增加数据的安全性,但是有些数据却往往是共享的,人们可以在不同的场合通过不同的途径访问同一个数据对象。有时在无意之中的误操作会改变有关数据的状况,而这是人们所不希望出现的。既要使数据能在一定范围内共享,又要保证它不被任意修改,这时可以使用const,即把有关的数据定义为常量。

常对象

在定义对象时指定对象为常对象。常对象必须要有初值,如:

Time const t1(12,34,46); //t1是常对象

这样,在所有的场合中,对象t1中的所有数据成员的值都不能被修改。凡希望保证数据成员不被改变的对象,可以声明为常对象

定义常对象的一般形式为:

类名 const 对象名[(实参表列)];

也可以把const写在最左面:

const 类名 对象名[(实参表列)];

二者是等价。

如果一个对象被声明为常对象,则不能调用该对象的非const型的成员函数(除了由系统自动调用的隐式的构造函数和析构函数)。例如,对于定义的Time类,如果有:

const Time t1(10,15,36);  //定义常对象t1
t1.get_time( );           //企图调用常对象t1中的非const型成员函数,非法

这是为了防止这些函数会修改常对象中数据成员的值。不能仅依靠编程者的细心来保证程序不出错,编译系统充分考虑到可能出现的情况,对不安全的因素予以拦截。

编译系统只检查函数的声明,只要发现调用了常对象的成员函数,而且该函数未被声明为const,就报错,提请编程者注意。

引用常对象中的数据成员很简单,只需将该成员函数声明为const即可,比如:

void get_time( ) const; //将函数声明为const

这表示get_time是一个const型函数,即常成员函数。常成员函数可以访问常对象中的数据成员,但仍然不允许修改常对象中数据成员的值。

小结:常对象只能调用常成员函数

有时在编程时有要求,一定要修改常对象中的某个数据成员的值,ANSI C++考虑到实际编程时的需要,对此作了特殊的处理,对该数据成员声明为mutable,如:

mutable int count;

把count声明为可变的数据成员,这样就可以用声明为const的成员函数来修改它的值。

常对象成员

可以将对象的成员声明为const,包括常数据成员和常成员函数。

常数据成员

其作用和用法与一般常变量相似,用关键字const来声明常数据成员。常数据成员的值是不能改变的。 有一点要注意:只能通过构造函数的参数初始化表对常数据成员进行初始化。如在类体中定义了常数据成员hour:

const int hour;   //声明hour为常数据成员

不能采用在构造函数中对常数据成员赋初值的方法。因为在调用构造函数之前,类首先对hour对象调用其默认构造函数,使其初始化为空值。在C++中,int其实是一个类。

在类外定义构造函数,应写成以下形式:

Time::Time(int h):hour(h){}   //通过参数初始化表对常数据成员hour初始化

常对象的数据成员都是常数据成员,因此常对象的构造函数只能用参数初始化表对常数据成员进行初始化。

常成员函数

一般的成员函数可以引用本类中的非const数据成员,也可以修改它们。如果将成员函数声明为常成员函数,则只能引用本类中的数据成员,而不能修改它们,例如只用于输出数据等。如:

void get_time( ) const;   //注意const的位置在函数名和括号之后

const是函数类型的一部分,在声明函数和定义函数时都要有const关键字,在调用时不必加const,成员函数可以引用const数据成员,也可以引用非const的数据成员。const数据成员可以被const成员函数引用,也可以被非const的成员函数引用。

总结:

(1).如果在一个类中,有些数据成员的值允许改变,另一些数据成员的值不允许改变,则可以将一部分数据成员声明为const,以保证其值不被改变,可以用非const的成员函数引用这些数据成员的值,并可以修改非const数据成员的值。

(2).如果要求所有的数据成员的值都不允许改变,则可以将所有的数据成员声明为const,或将对象声明为const(常对象),然后用const成员函数引用数据成员,这样起到“双保险”的作用,切实保证了数据成员不被修改。

(3).如果已定义了一个常对象,只能调用其中的const成员函数,而不能调用非const,成员函数(不论这些函数是否会修改对象中的数据)。这是为了保证数据的安全。如果需要访问常对象中的数据成员,可将常对象中所有成员函数都声明为const成员函数,但应确保在函数中不修改对象中的数据成员。不要误认为常对象中的成员函数都是常成员函数。常对象只保证其数据成员是常数据成员,其值不被修改。如果在常对象中的成员函数未加const声明,编译系统把它作为非const成员函数处理。常成员函数不能调用另一个非const成员函数

#include <iostream>

using namespace std;

class A{
public:
	A(int i,int j):a(i),b(j){}  //常成员数据只能通过初始化列表初始化

	int geta(){         //普通成员函数
		return a;
	}
	int getb(){         //普通成员函数
		return b;
	}

	int const_geta()const{   //常成员函数
		return a; 
	}

	int const_getb()const{   //常成员函数
		return b;
	}

	const int a;             //常成员数据
	int b;                   //普通成员数据
};

int main(){
	A a1(1,2);               //声明普通对象

	//普通成员函数可以访问常成员数据
	cout<<a1.geta()<<" "<<a1.getb()<<endl;

	const A a2(3,4);         //声明常对象

	//常对象不可调用普通成员函数 该行出错
	cout<<a2.geta()<<" "<<a2.getb()<<endl;   

	//常成员函数可以访问普通成员数据 也可以访问常成员数据
	cout<<a2.const_geta()<<" "<<a2.const_getb()<<endl;

	system("pause");
}
#include <iostream>
using namespace std;

class A{
public:
	A(int n):Anum(n){}
	void shownum(){
		cout<<Anum<<endl;
	}
	int Anum;
};

class B{
public:
	B(int n){
		Bnum=n;
	}
	void shownum() const{
		cout<<Bnum<<endl;
	}
	int Bnum;
};

class C{
public:
	C(int n,int x):Cnum1(n),Cnum2(x){}   //常数据成员只能用初始化列表初始化
	void shownum() const{                //常成员函数既可以访问常数据成员 也可访问非常数据成员
		cout<<Cnum1<<" "<<Cnum2<<endl;
	}
	void display(){
		cout<<Cnum1<<endl;
	}
	const int Cnum1;
	int Cnum2;
};

int main(){
	const A a(2);
	a.shownum();        //编译报错 常对象不能调用非常成员函数

	const B b(2);
	b.shownum();        //编译通过 shownum为B类常成员函数

	C c(3,4);
	c.shownum();        //编译通过
	c.display();        //普通成员函数既可以访问常数据成员 也可访问非常数据成员
	system("pause");
}

指向对象的指针常量

将指针变量声明为const这样指针值始终保持为其初值,不能改变。如:

Time t1(10,12,15),t2;     //定义对象
Time * const ptr1=&t1;    //const位置在指针变量名前面,规定ptr1的值是常值
                          //ptr1指向对象t1,此后不能再改变指向
ptr1=&t2;                 //错误,ptr1不能改变指向

定义指向对象的指针常量的一般形式为:

类名 * const 指针变量名=赋值;

指向对象的指针常量的值不能改变,即始终指向同一个对象,但可以改变其所指向对象(如t1)的值。

如果想将一个指针变量固定地与一个对象相联系(即该指针变量始终指向一个对象),可以将它指定为const型指针变量。往往用指针常量作为函数的形参,目的是不允许在函数执行过程中改变指针变量的值,使其始终指向原来的对象。

指向常对象的常量指针

定义一个指向常变量的常量指针:

const char *ptr;

注意const的位置在最左侧,它与类型名char紧连,表示指针变量ptr指向的char变量是常变量,不能 通过*ptr来改变其值的。

定义指向常变量的指针变量的一般形式为:

const 类型名 * 指针变量名;

说明:

(1).如果一个变量已被声明为常变量,只能用指向常变量的指针变量指向它,而不能用一般的(指向非const型变量的)指针变量去指向它。

(2).指向常变量的指针变量除了可以指向常变量外,还可以指向未被声明为const的变量。

(3).如果函数的形参是指向非const型变量的指针,实参只能用指向非const变量的指针,而不能用指向const变量的指针。

如果函数的形参是指向const型变量的指针,在执行函数过程中显然不能改变指针变量所指向的变量的值,因此允许实参是指向const变量的指针,或指向非const变量的指针。

指向常变量的指针变量可以指向const和非const型的变量,而指向非const型变量的指针变量只能指向非const的变量。

因此有:

(1).如果一个对象已被声明为常对象,只能用指向常对象的指针变量指向它,而不能用一般的(指向非const型对象的)指针变量去指向它。

(2).如果定义了一个指向常对象的指针变量,并使它指向一个非const的对象,则其指向的对象是不能通过指针来改变的。

(3).指向常对象的指针最常用于函数的形参,目的是在保护形参指针所指向的对象,使它在函数执行过程中不被修改。

当希望在调用函数时对象的值不被修改,就应当把形参定义为指向常对象的指针变量,同时用对象的地址作实参(对象可以是const或非const型)。如果要求该对象不仅在调用函数过程中不被改变,而且要求它在程序执行过程中都不改变,则应把它定义为const型。

(4).如果定义了一个指向常对象的指针变量,是不能通过它改变所指向的对象的值的,但是指针变量本身的值是可以改变的。

#include <iostream>

using namespace std;

class A{
public:
	A(int i,int j):a(i),b(j){}  
	int a;             
	int b;                 
};

int main(){

	A a(1,2);
	A const *ptr=&a;
	(*ptr).a=3;        //出错 不能通过常量指针来修改对象的值
	ptr->a=3;          //出错 不能通过常量指针来修改对象的值
	A b(3,4);
	ptr=&b;            //常量指针的值可以改变
	cout<<ptr->a<<" "<<ptr->b<<endl;
	system("pause");
}

对象的常引用

一个变量的引用就是变量的别名。实质上,变量名和引用名都指向同一段内存单元。如果形参为变量的引用名,实参为变量名,则在调用函数进行虚实结合时,并不是为形参另外开辟一个存储空间(常称为建立实参的一个拷贝),而是把实参变量的地址传给形参(引用名),这样引用名也指向实参变量。

#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的值,可以把引用变量t声明为const(常引用),函数原型为:

void fun(const Time &t);

则在函数中不能改变t的值,也就是不能改变其对应的实参t1的值。

在C++面向对象程序设计中,经常用常指针和常引用作函数参数。这样既能保证数据安全,使数据不能被随意修改,在调用函数时又不必建立实参的拷贝。用常指针和常引用作函数参数,可以提高程序运行效率。

常量引用(常引用)和常量指针(常指针)用法是一样的,二者可以类比:

#include <iostream>

using namespace std;

class A{
public:
	A(int i){
		m=i;
	}
	int m;
};

int main()
{
	A a1(1);
	A &b1=a1;             //声明一个A类的引用并赋值a1
	b1.m=100;             //改变b1也就改变a1
	cout<<a1.m<<" "<<b1.m<<endl;   

	A a2_1(2);
	const A a2_2(2);
	const A &b2_1=a2_1;   //常对象引用指向一个普通对象
	const A &b2_2=a2_2;   //常对象引用指向一个常对象
	b2_1.m=100;           //不能用常对象引用修改指向的内容
	a2_2.m=100;           //常对象内容不可修改
	b2_1=a1;              //引用一旦初始化不可再赋值

	const A a3(3);
	A &b3=a3;             //不可用普通引用来指向常对象 常对象只能用常对象引用来指向它

	system("pause");
}

普通对象引用不能用来指向常对象,应当使用常对象引用来指向它,这就相当于普通指针不能指向常变量,应当使用常量指针来指向它。

Search

    Post Directory