這些對象并不”抽象“,它們像int和float一樣實(shí)際
——Doug Mcllroy
C++類概念的目標(biāo)是提供一種建立新類型的工具,使得新類型的使用像內(nèi)部類型一樣方便。此外,派生類和模板提供了組織的方法,使得類與類之間的關(guān)聯(lián)更為密切
一個類型是某概念的具體體現(xiàn),例如C++內(nèi)部類型float及其四則運(yùn)算是對數(shù)學(xué)中實(shí)數(shù)概念的一個具體近似,一個類就是一個用戶定義類型。這一節(jié)我們學(xué)習(xí)定義類,創(chuàng)建類的對象,操作類的對象基本功能
定義類
類的定義十分簡單,就像下面這樣
class <classname>{
//...
};
{}里包括起來的,有這個類的屬性以及成員函數(shù),我們還通過關(guān)鍵字private, public, protected控制訪問權(quán)限。
以一個具體的Date為例,它是這樣定義的
class Date
{
private:
int day, month, year; //Date對象具有day, month, year這些屬性,它們是int類型的
char * description=" "; //Date對象還有description屬性,是一個字符串
public:
void init(int day, int month, int year, char *description) //這是一個成員函數(shù)
{
this->day = day;
//this 是一個特殊的指針,這一點(diǎn)在下面說明,
//這條語句是將形參day賦值給Date對象中的day
this->month = month;
this->year = year;
this->description = description;
}
void print() //這又是一個成員函數(shù),定義起來和普通函數(shù)無異
{
cout<<setw(4)<<"Year"<<setw(8)<<"Month"<<setw(10)<<"Day"<<endl;
cout<<setw(2)<<this->year<<setw(8)<<this->month<<setw(8)<<this->day;
if (description) cout<<"\nThat day is: "<<description;
}
};
創(chuàng)建對象
我們可以通過
<classname> object_name;
這樣的語法實(shí)例化一個類,即是創(chuàng)建一個這個類的對象,一個特定類的對象意味著我們可以該對象有該類的屬性以及成員函數(shù)。
通過點(diǎn)號(.)標(biāo)記法,我們可以訪問該對象的屬性值,也可以調(diào)用成員函數(shù),過程就像下面這樣
Date my_date;
my_date.init(8, 3, 2019, "Women's day");
my_date.print();
在這里,調(diào)用print()函數(shù)會把對象的屬性等顯示出來,輸出結(jié)果就是
Year Month Day
2019 3 8
That day is: Women's day
創(chuàng)建類的一些細(xì)節(jié)
以上就是關(guān)于類的非?;A(chǔ)的內(nèi)容,下面我們關(guān)注在創(chuàng)建一個類的過程中一些細(xì)節(jié)。
-
this指針
對于一般的類成員函數(shù),它們都只涉及到一個對象,即是調(diào)用它的對象,當(dāng)我們對該對象的屬性進(jìn)行訪問時,實(shí)際有一個this指針在這里起到作用,這種作用在涉及到兩個對象時尤為關(guān)鍵,
注意下面對Date的一點(diǎn)改進(jìn),我們定義了cmp_year()函數(shù)去比較兩個Date對象中哪一個年份更大.由于涉及到兩個對象,用this指針更加明確
...
bool cmp_year(Date & d)
{
return (this->year > d.year);
}
...
Date my_new_date = Date(2019, 3, 9);
Date yr_new_date(2018, 5, 1, "Labor's day");
if(my_new_date.cmp_year(yr_new_date)){
cout<<"My_New_Date is Bigger.";
}else{
cout<<"My_New_Date is Smaller";
}
...
-
訪問控制:首先我們關(guān)注
private,public這兩個訪問標(biāo)識符,private標(biāo)識下的成員無法被外界訪問(除去一些將在之后說明的特殊情況),例如
void main(){
Date my_date; //這里是將Date類實(shí)例化,產(chǎn)生對象my_date
my_date.day = 1; //試圖對my_date的day屬性賦值,出現(xiàn)錯誤
...
}
這樣做便是錯誤的,因?yàn)?code>day是私有的,無法被外界訪問.
-
構(gòu)造函數(shù):在
Date的創(chuàng)建過程中,有一個init函數(shù)顯然是為了初始化對象的,實(shí)際上使用構(gòu)造函數(shù)可以更準(zhǔn)確地做到這一點(diǎn)
我們對Date類進(jìn)行下面的改造,
...
public:
Date(int year, int month, int day)
{
this->year = year;
this->day = day;
this->month = month;
}
Date(int year, int month, int day, char * description)
{
this->day = day;
this->month = month;
this->year = year;
this->description = description;
}
Date()
{
this->day = 1;
this->month = 1;
this->year = 1;
this->description = "Default Setting";
}
上面的成員函數(shù)Date()就是構(gòu)造函數(shù),我們可以發(fā)現(xiàn)構(gòu)造函數(shù)的特點(diǎn):
- 不寫返回值
- 必須與該類同名
此外,示例代碼還告訴我們函數(shù)重載在這里同樣有效,實(shí)際上重載的規(guī)則也是一致的
當(dāng)有構(gòu)造函數(shù)且要初始化對象時,可以像下面這樣做
Date my_new_date = Date(2019, 3, 9);
//嚴(yán)格形式,這里的構(gòu)造函數(shù)對應(yīng)Date(int year, int month, int day)
Date yr_new_date(2019, 5, 1, "Labor's day");
//簡寫,對應(yīng)Date(int year, int month, int day, char * description)
Date default_date;
//這里沒有向構(gòu)造函數(shù)傳值,所以調(diào)用的是沒有參數(shù)的函數(shù)Date()
對三個對象均調(diào)用print(),有如下結(jié)果:
Year Month Day
2019 3 9
That day is:
Year Month Day
2019 5 1
That day is: Labor's day
Year Month Day
1 1 1
That day is: Default Setting
還可以對構(gòu)造函數(shù)進(jìn)一步細(xì)分,有兩類特殊的構(gòu)造函數(shù)值得注意:
- 默認(rèn)構(gòu)造函數(shù)
它是在未提供顯式初始值時用來創(chuàng)建對象的構(gòu)造函數(shù),像是這個語句Date my_date,實(shí)際上C++提供了默認(rèn)構(gòu)造函數(shù),是隱式的,不做任何工作,對于Date而言,默認(rèn)構(gòu)造函數(shù)如同Stock::Stock() { }
當(dāng)且僅當(dāng)沒有定義任何構(gòu)造函數(shù)時,編譯器才會提供默認(rèn)構(gòu)造函數(shù)。
- 析構(gòu)函數(shù):構(gòu)造函數(shù)在一個對象被初始化時被調(diào)用,而析構(gòu)函數(shù)則在某對象被銷毀時調(diào)用,邏輯都是一樣的,析構(gòu)函數(shù)的命名規(guī)則是:
~<classname>(){...};
這里不再具體給出示例
-
靜態(tài)成員:類里面的靜態(tài)成員以
static指明,這一成員較為特殊,一個static成員只有唯一的一份副本,而不像常規(guī)的非static成員那樣在每個在每個對象中均有一份副本,最重要的一是靜態(tài)成員(可以是某屬性也可以是某成員函數(shù)),不再屬于某一特定的對象,而是在整個類的作用域都起作用,只要在類中聲明靜態(tài)成員變量,即使不定義對象,也可以為靜態(tài)成員變量分配空間,進(jìn)而可以使用靜態(tài)成員變量。
看下面的例子
class Test
{
static int temp;
public:
static int getData()
{
return temp;
}
};
int Test::temp=3;
int main()
{
cout <<Test::getData()<<endl;
return 0;
}
運(yùn)行得到的結(jié)果是3,總結(jié)以下靜態(tài)成員的特點(diǎn):
- 用
static修飾 - 靜態(tài)成員必須在類外初始化,如例子中的
temp - 靜態(tài)函數(shù)可以被類調(diào)用,寫成
Test::getData()這樣的形式,普通的成員函數(shù)不可以被類直接調(diào)用;同樣可以通過類來獲取靜態(tài)成員(形式如Test::temp)而不可通過某對象獲取
再看一個更為實(shí)際的應(yīng)用,我們想要確定Date類到底創(chuàng)建了多少對象,可以做如下修改:
private:
...
static int obj_num; //聲明靜態(tài)變量obj_num用于存儲對象個數(shù)
public:
static int get_counted(){
return obj_num;
}
Date(int year, int month, int day)
{
...
obj_num++; //每次析構(gòu)函數(shù)執(zhí)行一次便將obj_num遞增一次
}
...
int Date::obj_num = 0; //靜態(tài)變量一定要在外圍聲明?。?!
int main()
{...
這是靜態(tài)成員的一種常見用法
-
常量成員函數(shù)
對于剛才Data類的例子,我們可以定義get()取值,這里可以使用常量成員函數(shù),如
...
int get_day() const { return this->day;}
通過使用const標(biāo)識,指明了這個函數(shù)不會修改Data的狀態(tài),像是下面這個函數(shù)就是有誤的
int get_day() const {
return this->day++; //錯誤:在const函數(shù)內(nèi)企圖修改成員值
}
-
友元
友元可以是函數(shù),也可以是類,所以有友元函數(shù),友元類的稱呼。友元函數(shù)是類定義中由關(guān)鍵字friend修飾的非成員函數(shù)。友元可以是普通函數(shù),也可以是其他類的成員函數(shù),但是在友元函數(shù)體中可以訪問到類的私有成員和保護(hù)成員。下面看一個簡單的例子
class TestCase1
{
private:
int t;
public:
TestCase1(int t=1)
{
this->t = t;
}
friend TestCase2 invite(TestCase1 obj);
};
class TestCase2
{
private:
char s;
public:
TestCase2(char s='a')
{
this->s = s;
}
get_ch()
{
return this->s;
}
};
TestCase2 invite(TestCase1 obj)
{
return obj.t<10? TestCase2():TestCase2('b');
}
int main()
{
TestCase1 t1();
TestCase1 t2(12);
TestCase2 t3=invite(t1);
TestCase2 t4=invite(t2);
cout<<t3.get_ch();
cout<<t4.get_ch();
}
···
###類作用域