1. 程式人生 > >C++基礎第三章(使用類和物件)中篇(物件陣列,物件指標,常物件)

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的值不能改變