類和物件(C++學習筆記 16)
- 類(class)是一種資料型別,是一種使用者定義的抽象的資料型別。
- 類是一種抽象的資料型別,它不佔儲存空間,不能容納具體的資料,因此在類宣告中不能給資料成員賦初值。
- 類代表了一批物件的共性和特徵。
- 類是進行封裝和資料隱藏的工具,它將資料與操作緊密地結合起來。
- 類是物件的抽象,物件是類的例項。
- C++保留C結構體這種資料型別,並對其功能進行了擴充,使結構體可以含有不同型別的資料,而且還可以含有函式。類與結構體的擴充形式非常相似。
- 類與結構體區別:在結構體中,如果對成員不作private 或public宣告,系統將其預設為公有的( public ),外界可以任意的訪問其中的資料成員和成員函式,它不具有資訊隱蔽的特性;而在類的宣告中,如果對其成員不作private 或public宣告,系統將其預設為私有的( private ),外界不可以訪問其中的資料成員和成員函式,它提供了預設的安全性。
- 一般來說,公有成員是類的對外介面,而私有成員是類的內部資料和內部實現,不希望外界訪問。
一、結構體
結構體--------統一操作一個整體物件。
結構體型別的定義形式:
struct [結構體型別名]
{
資料型別 成員1;
資料型別 成員2;
···
資料型別 成員n;
};
說明:
(1) 在需要該結構體變數的地方,用“ struct 結構名 變數名 ”進行變數定義。若省略結構名,則只能在型別定義的同時定義結構體變數,在程式的其他地方無法使用該結構體定義變數。
結構體變數的定義:
1)先定義結構體型別,再定義結構體型別變數。
struct stu_rec{
char cno[8]; //學號
char cname[10]; //學生姓名
char csex; //性別
int iscore[4]; //四科考試成績
};
struct stu_rec student1,student2; //定義結構體型別變數
通常,這樣的結構體型別定義放在函式的外部,程式的開始部分或標頭檔案中,變數的定義在函式中。
2)定義結構體型別的同時定義結構體型別變數。
struct date{
int iyear;
int imonth;
int iday;
}mydate1,mydate2;
在這種方式下,結構體型別定義通常在函式的外部,程式的開始部分。其後也可以使用1)的方式再定義其他結構體變數。
3)直接定義結構體型別變數。
struct
{
char cno[8];
char cname[10];
char csex;
float fb_salary,ff_salary,fp_salary; //基本工資、浮動工資、獎金
}person1,person2;
這種方式靈活性差,一般不用。
(2) 結構體型別中的成員型別可以是基本資料型別,也可以是其他資料型別。同一型別的成員可以在同一行定義,形式為:資料型別 成員名1,成員名2······
(3) 儲存一串字元應將其定義為字元陣列
(4) 結構體變數的成員引用
使用成員運算子“ . ”,引用的形式為:
<結構體型別變數名> . <成員名>
(5) 結構體型別變數的初始化
由於結構體型別變數可以彙集不同資料型別的成員,所以結構體型別變數的初始化必須在結構體型別變數的定義時進行。
例如: struct stu_info student = { “23333”, “xiao ming”, “f”, “20”,“computer science”}
(6) 指向結構體的指標
結構體變數所佔記憶體單元的起始地址就是該變數的指標。
可以定義一個指標變數來指向該結構體變數,並通過該指標變數來引用結構體變數成員。
1)結構體指標的定義
struct 結構體型別名 *指標變數名
例1:
struct stu_info *q; //q為指標變數,指向結構體型別stu_info
例2:
struct stu_cj
{
char cno[8];
char cname[10];
float fscore[4];
}stud,*p1,*p2; //p1,p2為指標變數,指向結構體型別stu_cj
變數的地址:&stud
成員的地址:&stud.cno、&stud.cname、&stud.fscore[0]
2)結構體指標對成員的引用
引用形式為:
指標變數->成員;
( *指標變數 ). 成員
例如: 指標變數p1、p2指向結構體變數x:p1=p2=&x;
通過結構體指標p1、p2來引用結構體變數x成員,以下三種方式是等價的:
x.cno、x.cname、x.fscore[0]
p1->cno、p1->cname、p1->fscore[0]
(*p2).cno、(*p2).cname、(*p2).fscore[0]
二、結構體的擴充
使結構體可以含有不同型別的資料,而且還可以含有函式
例如,下面聲明瞭一個擴充的結構體Complex,用這個擴充的結構體型別求複數的絕對值:
#include<iostream>
#include<cmath>
using namespace std;
struct Complex{
double real; //複數的實部
double imag; //複數的虛部
void init(double r,double i) //定義函式init,給real和imag賦初值
{
real=r;
imag=i;
}
double abscomplex(){ //定義函式abscomplex,求複數的絕對值
double t;
t=real*real+imag*imag;
return sqrt(t);
}
};
int main()
{
Complex A;
A.init(1.1,2.2);
cout<<"複數的絕對值是:"<<A.abscomplex()<<endl;
return 0;
}
在這個結構體中,real 和imag 是資料成員,函式init 和 abscomplex 是成員函式。
三、類的宣告
類與結構體的擴充形式十分相似。C++規定,在預設情況下,類中的成員是私有的,結構體中的成員是公有的。
(1)類宣告的一般形式如下:
class 類名{
[private:]
私有資料成員和成員函式
public:
公有資料成員和成員函式
};
(2)類中的成員:
私有成員(用private宣告): 包括資料成員和成員函式,只能被類內的成員函式訪問,而不能被類外的物件訪問。這樣,私有成員就整個隱蔽在類中,在類的外部,物件無法直接訪問它們,實現了訪問許可權的有效控制。
公有成員(用public宣告): 既可被類內的成員函式訪問,也可被類外的物件訪問。這部分的資料成員和成員函式稱為類的公有成員,公有成員對外是完全開放的,公有成員既可以被本類的成員函式訪問,也可以在類外被該類的物件訪問,公有成員函式是類與外界的介面,來自類外部的對私有成員的訪問需要通過這些介面來進行。
保護成員(用protected宣告): 可以由本類的成員函式訪問,也可以由本類的派生類的成員函式訪問, 而類外的任何訪問都是非法的,即它是半隱蔽的。
說明:
- 如果一個類的所有成員都宣告為私有的,即只有私有部分,那麼該類將完全與外界隔絕,這樣,雖然資料“安全”的目的達到了,但是這樣的類是沒有實際意義的。在實際應用中,一般把需要保護的資料設定為私有的,把它隱蔽起來,而把成員函式設定為公有的,作為類與外界的介面。
- 一般情況下,一個類的資料成員應該宣告為私有成員,成員函式宣告為公有成員。這樣,內部的資料隱蔽在類中,在類前面的外部無法直接訪問,使資料得到有效的保護,也不會對該類以外的其餘部分造成影響,程式模組之間的相互作用就被降低到最小。
- 類宣告中的 private、protect 和 public 三個關鍵字可以按任意順序出現任意次,但是,如果把所有的私有成員和公有成員歸類放在一起,程式更加清晰。
- 資料成員可以是任何資料型別,但不能是自動(auto)、暫存器(register)或外部(extern)進行說明。
auto/register/extern表示的是變數的儲存位置和作用域。auto變數儲存在函式的堆疊空間,register儲存在暫存器,extern表示這裡沒有新定義變數,只是擴充套件了一個已有全域性變數的作用域。類和結構體中的變數是成員變數,其儲存位置和作用域由定義物件的函式決定,不由這些物件自己決定。
(3)成員函式的定義
在C++中,成員函式既可以定義為普通的成員函式(即非內聯的成員函式),也可以定義為內聯成員函式。以下有3種定義方式,方式1是定義普通成員函式,方式2和3是定義成內聯成員函式。
1)定義方式1:
在類宣告中只給出成員函式的原型,而將成員函式的定義放在類的外部。(即類內宣告,類外定義)
這種成員函式在類外定義的一般形式為:
返回值型別 類名::成員函式名(引數表)
{
函式體
}
例如, 表示座標點的類Point 可以宣告如下:
#include<iostream>
using namespace std;
class Point{
public:
void setpoint(int,int); //座標點的成員函式setpoint的函式原型
int getx(); //取x座標點的成員函式getx的函式原型
int gety(); //取y座標點的成員函式gety的函式原型
private:
int x,y;
};
void Point::setpoint(int a,int b) //在類外定義成員函式setpoint
{
x=a;
y=b;
}
int Point::getx() //在類外定義成員函式getx
{
return x;
}
int Point::gety() //在類外定義成員函式getx
{
return y;
}
說明:
①“::”是作用域運算子,用於宣告這個成員函式是屬於哪個類的,如“Point::”,說明這些成員函式是屬於類Point的。
2)定義方式2:
將成員函式直接定義在類的內部。
此時,C++編譯器將函式setpoint、getx、gety作為行內函數進行處理,即將這些函式隱含地定義為內聯成員函式。由於沒有使用關鍵字inline,因此成為隱式定義。
例如,
class Point{
public:
void setpoint(int a,int b) //座標點的成員函式setpoint的函式原型
{
x=a;
y=b;
}
int getx() //取x座標點的成員函式getx的函式原型
{
return x;
}
int gety() //取y座標點的成員函式gety的函式原型
{
return y;
}
private:
int x,y;
};
3)定義方式3:
在類宣告中只給出成員函式的原型,而將成員函式的定義放在類的外部,但是在類內函式原型宣告前或在類外定義成員函式前加上關鍵字“inline”,來顯式地說明這是一個行內函數。稱為顯示定義。
例如,
class Point{
public:
inline void setpoint(int a,int b); //宣告函式setpoint為行內函數
inline int getx(); //宣告成員函式getx為行內函數
inline int gety(); //宣告成員函式gety為行內函數
private:
int x,y;
};
inline void Point::setpoint(int a,int b){
x=a;
y=b;
}
inline int Point::getx(){
return x;
}
inline int Point::gety(){
return y;
}
四、類和物件的關係
在C++中,把具有相同資料和相同操作集的物件看成是同一類。
一個類也就是使用者宣告的一個數據型別。在程式中定義的每一個變數都是其所屬資料型別的一個例項。類的物件可以看成是該類型別的一個例項,定義一個物件和定義一個一般變數相似。
五、物件的定義
可以用一下兩種方法定義物件:
(1)在宣告類的同時,直接定義物件
即在宣告類的右花括號“ } ” 後,直接寫出屬於該類的物件名錶。
例如,在宣告類Point 的同時,直接定義了物件op1 和op2。
class Point{
public:
void setpoint(int,int);
int getx();
int gety();
private:
int x,y;
}op1,op2;
(2)聲明瞭類之後,在使用時再定義物件
定義的一般形式為:類名 物件名1 物件名2 ······
例如,定義op1和op2為Point 類的兩個物件:
Point op1,op2;
六、物件中成員的訪問
公有的資料成員和成員函式,在類的外部可以通過類的物件進行訪問。訪問物件中的成員通常有三種方法:
(1)通過物件名和物件選擇符(“.”,簡稱為點運算子)訪問物件中的成員。
一般形式為:物件名.資料成員名 或 物件名.成員函式名[(實參表)]
例如,訪問物件中的成員
#include<iostream>
using namespace std;
class Point{
public:
inline void setpoint(int a,int b); //宣告函式setpoint為行內函數
inline int getx(); //宣告成員函式getx為行內函數
inline int gety(); //宣告成員函式gety為行內函數
private:
int x,y;
};
inline void Point::setpoint(int a,int b){
x=a;
y=b;
}
inline int Point::getx(){
return x;
}
inline int Point::gety(){
return y;
}
int main(){
Point A;
A.setpoint(2,3);
cout<<A.getx()<<" "<<A.gety()<<endl;
return 0;
}
(2)通過指向物件的指標訪問物件中的成員
class Date{
public:
int year;
};
int main(){
Date d,*ptr; //定義物件d和指向類Date的指標變數ptr
ptr=&d; //使ptr指向物件d
cout<<ptr->year; //輸出ptr指向物件中的成員year
return 0;
}
在這個例子中,
d.year ,(*ptr).year ,ptr->year 這三者是等價的。
(3)通過物件的引用訪問物件中的成員
為一個物件定義了一個引用,也就是為這個物件起了個別名,因此完全可以通過引用來訪問物件中的成員,其方法與通過物件名來訪問物件中的成員是相同的。
例如,
class Date{
public:
int year;
};
int main(){
Date d1;
Date &d2=d1;
cout<<d1.year;
cout<<d2.year;
}
七、類的作用域和類成員的訪問屬性
類的作用域: 指在類的宣告中的一對花括號所形成的作用域。一個類的所有成員都在該類的作用域內。在類的作用域中,一個類的任何成員可以不受限制地訪問該類中的其他成員。而在類的作用域外,對該類的資料成員和成員函式的訪問則要受到一定的限制,有時甚至是不允許的,這與類成員的訪問屬性有關。
類成員的訪問屬性: 說明為公有的成員不但可以被類中的成員函式訪問,還可在類的外部,通過類的物件進行訪問;說明為私有的成員只能被類中的成員函式訪問,不能在類的外部,通過類的物件進行訪問。
一般來說,公有成員是類的對外介面,而私有成員是類的內部資料和內部實現,不希望外界訪問。