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