其中 class_name 是類的名稱 (用戶自定義的類型) ,而可選項object_name
是一個或幾個對象(object)標(biāo)識。Class的聲明體中包含成員members,成員可以是數(shù)據(jù)或函數(shù)定義,同時也可以包括允許范圍標(biāo)志 permission
labels,范圍標(biāo)志可以是以下三個關(guān)鍵字中任意一個:private:, public: 或 protected:。它們分別代表以下含義:
class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void);
} rect;
上面例子定義了一個class CRectangle 和該class類型的對象變量rect 。這個class 有4個成員:兩個整型變量 (x 和 y)
,在private 部分 (因為private 是默認的允許范圍);以及兩個函數(shù), 在 public 部分:set_values() 和
area(),這里只包含了函數(shù)的原型(prototype)。
注意class名稱與對象(object)名稱的不同:在上面的例子中,CRectangle 是class 名稱
(即用戶定義的類型名稱),而rect 是一個CRectangle類型的對象名稱。它們的區(qū)別就像下面例子中類型名 int和 變量名a
的區(qū)別一樣:
int a;
int 是class名稱 (類型名) ,而a 是對象名 object name (變量)。
在程序中,我們可以通過使用對象名后面加一點再加成員名稱(同使用C structs一樣),來引用對象rect
的任何public成員,就像它們只是一般的函數(shù)或變量。例如:
rect.set_value (3,4);
myarea = rect.area();
但我們不能夠引用 x 或 y ,因為它們是該class的 private 成員,它們只能夠在該class的其它成員中被引用。暈了嗎?下面是關(guān)于class
CRectangle的一個復(fù)雜的例子:
#include <iostream.h>
class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void) {return (x*y);}
};
void CRectangle::set_values (int a, int b) {
x = a;
y = b;
}
int main () {
CRectangle rect;
rect.set_values (3,4);
cout << "area: " << rect.area();
}
|
area: 12 |
上面代碼中新的東西是在定義函數(shù)set_values().使用的范圍操作符(雙冒號::
)。它是用來在一個class之外定義該class的成員。注意,我們在CRectangle class內(nèi)部已經(jīng)定義了函數(shù)area()
的具體操作,因為這個函數(shù)非常簡單。而對函數(shù)set_values()
,在class內(nèi)部只是定義了它的原型prototype,而其實現(xiàn)是在class之外定義的。這種在class之外定義其成員的情況必須使用范圍操作符::。
范圍操作符 (::)
聲明了被定義的成員所屬的class名稱,并賦予被定義成員適當(dāng)?shù)姆秶鷮傩?,這些范圍屬性與在class內(nèi)部定義成員的屬性是一樣的。例如,在上面的例子中,我們在函數(shù)set_values()
中引用了private變量x 和 y,這些變量只有在class內(nèi)部和它的成員中才是可見的。
在class內(nèi)部直接定義完整的函數(shù),和只定義函數(shù)的原型而把具體實現(xiàn)放在class外部的唯一區(qū)別在于,在第一種情況中,編譯器(compiler)
會自動將函數(shù)作為inline 考慮,而在第二種情況下,函數(shù)只是一般的class成員函數(shù)。
我們把 x 和 y 定義為private 成員 (記住,如果沒有特殊聲明,所有class的成員均默認為private
),原因是我們已經(jīng)定義了一個設(shè)置這些變量值的函數(shù) (set_values())
,這樣一來,在程序的其它地方就沒有辦法直接訪問它們。也許在一個這樣簡單的例子中,你無法看到這樣保護兩個變量有什么意義,但在比較復(fù)雜的程序中,這是非常重要的,因為它使得變量不會被意外修改
(這里意外指的是從object的角度來講的意外)。
使用class的一個更大的好處是我們可以用它來定義多個不同對象(object)。例如,接著上面class
CRectangle的例子,除了對象rect之外,我們還可以定義對象rectb :
#include <iostream.h>
class CRectangle {
int x, y;
public:
void set_values (int,int);
int area (void) {return (x*y);}
};
void CRectangle::set_values (int a, int b) {
x = a;
y = b;
}
int main () {
CRectangle rect, rectb;
rect.set_values (3,4);
rectb.set_values (5,6);
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
}
|
rect area: 12 rectb area: 30
|
注意: 調(diào)用函數(shù)rect.area()
與調(diào)用rectb.area()所得到的結(jié)果是不一樣的。這是因為每一個class CRectangle 的對象都擁有它自己的變量 x 和
y,以及它自己的函數(shù)set_value() 和 area()。
這是基于對象( object) 和 面向?qū)ο缶幊?(object-oriented
programming)的概念的。這個概念中,數(shù)據(jù)和函數(shù)是對象(object)的屬性(properties),而不是像以前在結(jié)構(gòu)化編程 (structured
programming) 中所認為的對象(object)是函數(shù)參數(shù)。在本節(jié)及后面的小節(jié)中,我們將討論面向?qū)ο缶幊痰暮锰帯?/p>
在這個具體的例子中,我們討論的class (object的類型)是CRectangle,有兩個實例(instance),或稱對象(object):rect
和 rectb,每一個有它自己的成員變量和成員函數(shù)。
構(gòu)造函數(shù)和析構(gòu)函數(shù) (Constructors and destructors)
對象(object)在生成過程中通常需要初始化變量或分配動態(tài)內(nèi)存,以便我們能夠操作,或防止在執(zhí)行過程中返回意外結(jié)果。例如,在前面的例子中,如果我們在調(diào)用函數(shù)set_values(
) 之前就調(diào)用了函數(shù)area(),將會產(chǎn)生什么樣的結(jié)果呢?可能會是一個不確定的值,因為成員x 和 y 還沒有被賦于任何值。
為了避免這種情況發(fā)生,一個class 可以包含一個特殊的函數(shù):構(gòu)造函數(shù)
constructor,它可以通過聲明一個與class同名的函數(shù)來定義。當(dāng)且僅當(dāng)要生成一個class的新的實例
(instance)的時候,也就是當(dāng)且僅當(dāng)聲明一個新的對象,或給該class的一個對象分配內(nèi)存的時候,這個構(gòu)造函數(shù)將自動被調(diào)用。下面,我們將實現(xiàn)包含一個構(gòu)造函數(shù)的CRectangle
:
#include <iostream.h>
class CRectangle {
int width, height;
public:
CRectangle (int,int);
int area (void) {return (width*height);}
};
CRectangle::CRectangle (int a, int b) {
width = a;
height = b;
}
int main () {
CRectangle rect (3,4);
CRectangle rectb (5,6);
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
}
|
rect area: 12 rectb area: 30
|
正如你所看到的,這個例子的輸出結(jié)果與前面一個沒有區(qū)別。在這個例子中,我們只是把函數(shù)set_values換成了class的構(gòu)造函數(shù)constructor。注意這里參數(shù)是如何在class實例
(instance)生成的時候傳遞給構(gòu)造函數(shù)的:
CRectangle rect (3,4);
CRectangle rectb
(5,6);
同時你可以看到,構(gòu)造函數(shù)的原型和實現(xiàn)中都沒有返回值(return value),也沒有void
類型聲明。構(gòu)造函數(shù)必須這樣寫。一個構(gòu)造函數(shù)永遠沒有返回值,也不用聲明void,就像我們在前面的例子中看到的。
析構(gòu)函數(shù)Destructor
完成相反的功能。它在objects被從內(nèi)存中釋放的時候被自動調(diào)用。釋放可能是因為它存在的范圍已經(jīng)結(jié)束了(例如,如果object被定義為一個函數(shù)內(nèi)的本地(local)對象變量,而該函數(shù)結(jié)束了);或者是因為它是一個動態(tài)分配的對象,而被使用操作符delete釋放了。
析構(gòu)函數(shù)必須與class同名,加水波號tilde (~) 前綴,必須無返回值。
析構(gòu)函數(shù)特別適用于當(dāng)一個對象被動態(tài)分別內(nèi)存空間,而在對象被銷毀的時我們希望釋放它所占用的空間的時候。例如:
#include <iostream.h>
class CRectangle {
int *width, *height;
public:
CRectangle (int,int);
~CRectangle ();
int area (void) {return (*width * *height);}
};
CRectangle::CRectangle (int a, int b) {
width = new int;
height = new int;
*width = a;
*height = b;
}
CRectangle::~CRectangle () {
delete width;
delete height;
}
int main () {
CRectangle rect (3,4), rectb (5,6);
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
return 0;
}
|
rect area: 12 rectb area: 30
|
構(gòu)造函數(shù)重載(Overloading Constructors)
像其它函數(shù)一樣,一個構(gòu)造函數(shù)也可以被多次重載(overload)為同樣名字的函數(shù),但有不同的參數(shù)類型和個數(shù)。記住,編譯器會調(diào)用與在調(diào)用時刻要求的參數(shù)類型和個數(shù)一樣的那個函數(shù)(Section
2.3, Functions-II)。在這里則是調(diào)用與類對象被聲明時一樣的那個構(gòu)造函數(shù)。
實際上,當(dāng)我們定義一個class而沒有明確定義構(gòu)造函數(shù)的時候,編譯器會自動假設(shè)兩個重載的構(gòu)造函數(shù) (默認構(gòu)造函數(shù)"default constructor"
和復(fù)制構(gòu)造函數(shù)"copy constructor")。例如,對以下class:
class CExample {
public:
int a,b,c;
void multiply (int n, int m) { a=n; b=m; c=a*b; };
};
沒有定義構(gòu)造函數(shù),編譯器自動假設(shè)它有以下constructor 成員函數(shù):
必須注意:這兩個默認構(gòu)造函數(shù)(empty construction 和 copy constructor
)只有在沒有其它構(gòu)造函數(shù)被明確定義的情況下才存在。如果任何其它有任意參數(shù)的構(gòu)造函數(shù)被定義了,這兩個構(gòu)造函數(shù)就都不存在了。在這種情況下,如果你想要有empty
construction 和 copy constructor ,就必需要自己定義它們。
當(dāng)然,如果你也可以重載class的構(gòu)造函數(shù),定義有不同的參數(shù)或完全沒有參數(shù)的構(gòu)造函數(shù),見如下例子:
#include <iostream.h>
Class CRectangle {
int width, height;
public:
CRectangle ();
CRectangle (int,int);
int area (void) {return (width*height);}
};
CRectangle::CRectangle () {
width = 5;
height = 5;
}
CRectangle::CRectangle (int a, int b) {
width = a;
height = b;
}
int main () {
CRectangle rect (3,4);
CRectangle rectb;
cout << "rect area: " << rect.area() << endl;
cout << "rectb area: " << rectb.area() << endl;
}
|
rect area: 12 rectb area: 25
|
在上面的例子中,rectb 被聲明的時候沒有參數(shù),所以它被使用沒有參數(shù)的構(gòu)造函數(shù)進行初始化,也就是width 和height 都被賦值為5。
注意在我們聲明一個新的object的時候,如果不想傳入?yún)?shù),則不需要寫括號():
CRectangle
rectb;
CRectangle rectb();
類的指針(Pointers to classes)
類也是可以有指針的,要定義類的指針,我們只需要認識到,類一旦被定義就成為一種有效的數(shù)據(jù)類型,因此只需要用類的名字作為指針的名字就可以了。例如:
CRectangle
* prect;
是一個指向class CRectangle類型的對象的指針。
就像數(shù)據(jù)機構(gòu)中的情況一樣,要想直接引用一個由指針指向的對象(object)中的成員,需要使用操作符
->。這里是一個例子,顯示了幾種可能出現(xiàn)的情況:
#include <iostream.h>
class CRectangle {
int width, height;
public:
void set_values (int, int);
int area (void) {return (width * height);}
};
void CRectangle::set_values (int a, int b) {
width = a;
height = b;
}
int main () {
CRectangle a, *b, *c;
CRectangle * d = new CRectangle[2];
b= new CRectangle;
c= &a;
a.set_values (1,2);
b->set_values (3,4);
d->set_values (5,6);
d[1].set_values (7,8);
cout << "a area: " << a.area() << endl;
cout << "*b area: " << b->area() << endl;
cout << "*c area: " << c->area() << endl;
cout << "d[0] area: " << d[0].area() << endl;
cout << "d[1] area: " << d[1].area() << endl;
return 0;
}
|
a area: 2 *b area: 12 *c area:
2 d[0] area: 30 d[1] area: 56 |
以下是怎樣讀前面例子中出現(xiàn)的一些指針和類操作符 (*, &, ., ->, [ ]):
- *x 讀作: pointed by x (由x指向的)
- &x 讀作: address of x(x的地址)
- x.y 讀作: member y of object x (對象x的成員y)
- (*x).y 讀作: member y of object pointed by x(由x指向的對象的成員y)
- x->y 讀作: member y of object pointed by x (同上一個等價)
- x[0] 讀作: first object pointed by x(由x指向的第一個對象)
- x[1] 讀作: second object pointed by x(由x指向的第二個對象)
- x[n] 讀作: (n+1)th object pointed by x(由x指向的第n+1個對象)
在繼續(xù)向下閱讀之前,一定要確定你明白所有這些的邏輯含義。如果你還有疑問,再讀一遍這一笑節(jié),或者同時參考 小節(jié) "3.3, 指針(Pointers)" 和
"3.5, 數(shù)據(jù)結(jié)構(gòu)(Structures)".
由關(guān)鍵字struct和union定義的類
類不僅可以用關(guān)鍵字class來定義,也可以用struct或union來定義。
因為在C++中類和數(shù)據(jù)結(jié)構(gòu)的概念太相似了,所以這兩個關(guān)鍵字struct和class的作用幾乎是一樣的(也就是說在C++中struct定義的類也可以有成員函數(shù),而不僅僅有數(shù)據(jù)成員)。兩者定義的類的唯一區(qū)別在于由class定義的類所有成員的默認訪問權(quán)限為private,而struct定義的類所有成員默認訪問權(quán)限為public。除此之外,兩個關(guān)鍵字的作用是相同的。
union的概念與struct和class定義的類不同,
因為union在同一時間只能存儲一個數(shù)據(jù)成員。但是由union定義的類也是可以有成員函數(shù)的。union定義的類訪問權(quán)限默認為public。