C++基础第三章(使用类和对象)中篇(对象数组,对象指针,常对象)

一.1,对象数组

我们定义普通类型的数组时

int a[100];
char b[100];
string s[100];

定义对象数组也是一样的

Student stud[5];        //定义stud数组,有5个元素

对象数组的初始化

Student::Student(int = 11, int = 8, int = 9);
Student stud[3] = {106, 5, 7};		//给每个对象提供第一个实参
Student stud[3] = {1, 2, 3, 4}		//不合法的,实参个数超过对象数组元素个数

对每个数组的是三个参数都初始化

Student Stu[3] = {	//定义对象数组。 
	Student(101, 1, 2);	//调用第一个元素的构造函数,向它提供3个参数 
	Student(102, 3, 4);	//调用第二个元素的构造函数,向它提供3个参数
	Student(103, 5, 6);	//调用第三个元素的构造函数,向它提供3个参数
}

注意:初始化每个对象时,直接用类名

一.2,对象指针

其实你定义一个 int a然后它在一个空间内,你可以用一个指针指向它(指针存放a的地址)这就指针。

对象指针给上述一样。定义一个指针变量存放对象的地址。

Time t;		//定义t是Time类的对象 
Time *pt;			//定义pt为指向Time类对象的指针变量 
pt = &t;			//将t的起始地址赋给pt; 

一般形式为:类名 *对象指针名

定义对象数据成员的指针变量一般形式为:

数据类型名 *指针变量名;

* pt;			//pt所指向的对象t 
(*pt).hour;		//pt所指向对象中的hour成员,即t.hour 
pt->hour;		//pt所指向对象中的hour成员,即t.hour 
(*pt).get_time();	//pt所指向对象中的get_time()函数,即t.get_time(); 
pt->get_time(); 	//pt所指向对象中的get_time()函数,即t.get_time();

2,3行等价。4,5行等价

一.3,指向对象成员的指针

指向对象数据成员的指针

int *p1;	    //定义指向整型的指针变量 
p1 = &t1.hour;	    //将对象t1的数据成员hour的地址赋值给p1,使p1指向t1.hour 
cout << *p << endl; //输出t1.hour 

类型名 (*指针变量名)(参数列表);

void (*p)();	//	p是指void型函数的指针变量 

可以p指向一个函数,并通过指针变量调用函数

p = fun;		//将fun函数的入口地址赋值给指针变量p,p就指向了函数fun 
(*p)(); 		//调用fun函数 

指向对象成员函数的指针变量  要匹配3方面(1)函数的参数类型和参数个数(2)函数返回的值的类型(3)所属的类

void (Time::*p2)();

数据类型名(类名::*指针变量)(参数列表);

也可以让它指向一个公用成员函数,一般形式为  指针变量 = &类名::成员函数名;

下面例子中涉及以上3个知识点

#include<iostream>
using namespace std;
class Time
{
	public:
	Time(int, int, int);
	int hour;
	int minute;
	int sec;
	void get_time();	//公有成员函数 
};
Time::Time(int h, int m, int s)
{
	hour = h;
	minute = m;
	sec = s;
}
void Time::get_time()
{
	cout << hour << ": " << minute << ": " << sec << endl;
}
int main ()
{
	Time t1(6,0,0);
	int *p1 = &t1.hour;		//定义指向整型数据的指针变量p1指向t1.hour; 
	cout << *p1 << endl; 	//输出t1.hour 
	
	t1.get_time();
	Time *p2 = &t1;		//定义指向Time类对象的指针p2,并使p2指向t1;		
	p2->get_time();		//调用p2所指向对象(即t1)的get_time函数 
	
	void(Time::*p3)();		//定义指向Time 类公用成员函数的指针变量p3; 
	p3 = &Time::get_time;	//使p3指向Time类公用成员函数get_time; 
	(t1.*p3)();				//调用对象t1中p3所指向的成员函数 
	return 0;
	
}

从上面看出  成员函数的入口地址为  类名::成员函数名

void(Time::*p3)();		//定义指向Time 类公用成员函数的指针变量p3; 
p3 = &Time::get_time;	//使p3指向Time类公用成员函数get_time; 

//上述两句可以合为
void(Time::*p3)() = &Time::get_time;

一.3,指向当前对象的 this 指针

在第二章中我们提到:每个对象中的数据成员都分别占有储存空间,如果对同一个类定义n个对象,则就有n个相同大小的空间,存放n个对象的数据成员,但是,不用的对象都调用同一个函数的目标代码。

#include<iostream>
using namespace std;
class Box 
{ 
	public:
		Box(int h = 10, int w = 10, int len = 10);
		int volume();
	private:
		int height;
		int width;
		int length;		
};
Box::Box(int h, int w, int len)
{
	height = h;
	width = w;
	length = len;
}
int Box::volume()
{
	return 	(height * width * length);
}
int main ()
{
	Box a, b, c;
}

那么当不同对象的成员函数引用数据时,怎么才能保证引用的是所指定对象的成员呢?假如对Box类  定义了  三个对象,a,b,c。如果有a.volume(),应该是引用对象中的a中的 height,width,length,计算出长方体a的体积。如果有b.volume()应该引用b中的 height,width,length,计算出长方体b的体积,而现在都用同一个函数代码,系统应该怎么使用它,分别引用a或b中的数据成员呢?

在每一个成员函数中都包含一个特殊的指针,这个指针的名字是固定的,称为this。它是指向本类对象的指针,他的值是当前被调用的成员函数所在的对象的起始地址。

例如当调用成员a.volume时,编译系统就把对象a的起始地址符给this指针,于是在成员函数引用数据成员时,就按照this的指向找到对象a的数据成员。例如volume函数要计算height * width * length的值 实际执行的是

this->height * this->width * this->length

由于this是指向a的又相当于

a.height * a.width * a.length
int Box::volume()
{
	return 	(height * width * length);
}

c++ 执行实际是

int Box::volume(Box *this)
{
	return 	(this->height * this->width * this->length);
}

即在成员函数的形参列表中增加一个this指针,在调用该成员函数是,实际上是用以下调用方式

a.volume(&a);

将对象a的地址传给形参this指针。然后按this的指向去引用其他成员。(这些都是编译系统自己实现的)

3个都是等价的

return 	(this->height * this->width * this->length);
return (height * width * length);
return ((*this).height * (*this).width * (*this).length);

一.4,公用数据的保护

(1)常对象

例如

Time const t1(12, 34, 56); 	//定义t1是常对象 

常对象的意思是,在t1生命周期中,t1中的所有数据成员的值都不被修改。所有你只要有希望保护的值(或者人啊 '_')都可以声明为常对象。

常对象定义的一般形式

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

或者

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

如果一个对象被声明为常对象,那么只能调用它的常成员函数,而不能调用该对象的普通成员函数。常成员函数是常对象对外的唯一接口。

const Time t1(10, 9, 8);	//定义对象t1为常对象
t1.get_time();			//企图调用对象t1中的普通成员函数,非法

这是为了防止普通成员函数会修改常对象中数据成员的值。就算在get_time函数内并没有修改常对象中的数据成员,也不行。因为编译系统会充分考虑到出现的情况,意思就是对不安全的因素以排斥;

有人会问:为什么编译系统不会专门检查函数的代码呢?看它是否修改了常对象中的数据成员的值内?实际上函数的定义与声明可能不在一个源程序文件中,而编译器真实以一个源文件为单位的,无法侧出两个源程序文件之间是否有矛盾,如果有错,只有在链接在运行阶段才能发现。这给试调带来不便。

简单来书说就是:编译系统只检查函数的声明,只要发现调用了常对象的成员函数,而且该函数未被声明为const,就会报错

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

常成员函数可以访问常对象中的数据成员,但不允许修改常对象中数据成员的值。

但是如果有编程要求,常对象中的某个数据成员的值也是可以改的,对数据声明为mutable

mutable int count; 

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

 

(2)常对象成员

(1)常数据成员

还是  const来声明,注意:只能通过构造函数的参数初始化表对常数据成员进行初始化,任何其他函数都不能对常数据成员赋值

const int hour;			//定义hour为常数据成员 
Time::Time (int h)		//非法,不能对之赋值 
{
	hour = h;	
} 

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

(2)常成员函数

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

例如只用于输出数据

void get_time() const;

类型名 函数名(参数表)const;

const是函数类型的一部分,声名和定义函数时都需要有const关键字,在调用时不需要。

常函数成员可以引用非const的数据成员,const数据成员可以被const成员函数引用,也可以被非const成员函数引用

数据成员 非const得普通成员函数 const成员函数
非const的普通数据成员 可以引用也可以改变值 可以引用,但不可以改变值
const数据成员 可以引用,但不能改变值 可以引用,但不可以改变值
const对象 不允许 可以引用,但不可以改变值

如果已经定义了一个常对象,只能调用其中的const成员函数,而不能调用非成员函数。这是为了保护数据的安全,如果需要访问常对象中的数据成员,可以将常对象中所有成员函数都声明为const成员函数,并确保在函数中不修改对象中的数据成员。

常成员函数不能调用另一个非const成员函数。

(3)指向对象的常指针

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

一般形式

类名 * const 指针变量名

(4)指向常对象的指针变量

一般形式,

const 类型名 *指针变量名;

如果一个人变量已被声名为常变量,只能用指向常变量的指针变量指向它,而不能用一般指针。

const char c[] = "boy";	//定义const 型的char数组 
const char *p1;			//定义p1指向const型的char变量的指针变量 
p1 = c;				//合法p1指向常变量 (char数组的首元素) 
char *p2 = c;		//不合法,p2不是指向常变量的指针变量 
 char c1 = 'a';	//定义字符变量c1,它并未声名为const 
const char *p;	//定义了一个指向变量的指针p; 
p = &c1;		//使p指向字符变量c1; 
*p = 'b';		//非法  , 不能通过p改变c1的值; 
c1 = 'b'; 		//合法,没有通过p访问c1,c1不是常变量 

上面这个例子。并不意味着吧c1也声名为常变量,而是在用指针变量访问c1期间,c1具有常变量特征,其值不能改变。但是在其他情况下 还是c1还是个变量,其值可以改变。

Time t1(10, 12, 15);		//定义Time类对象t1,它是非const型对象 
const Time *p = &t1;		//定义p是之指向常对象的指针变量,并指向t1 
t1.hour = 18;		//合法t1不是常变量 
(*p).hour = 18; 	//非法,不能通过改变指针变量的改变t1的值 
Time *const p;	//指向对象的常指针变量 
const Time *p; 	//指向常对象的指针变量 

(5)对象的常引用

void fun(Time &t){
    t.hour = 10;
}

如果不希望在函数中修改实参t1的值可以吧fun函数的形参t声明为const

void fun(const Time &t)

Const 小结

形式 含义
Time  const  t1; t1是常对象,其值在任何情况下下都不能改变
void Time : : fun() const;  是Time类中的常成员函数,可以引用,但是不能修改本类中的数据成员
Time * const p; p是指向Time类对象的常指针变量,p的值(p的指向)不能改变
const Time * p; p是指向Time类常对象的指针变量,p指向类对象的值不能通过p来改变
const Time &t1 = t; t1是Time类对象 t 的引用,二者指向同一存储空间,t的值不能改变

猜你喜欢

转载自blog.csdn.net/Harington/article/details/84204782
今日推荐