1. 程式人生 > >類和物件(C++學習筆記 16)

類和物件(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;
}

七、類的作用域和類成員的訪問屬性

類的作用域: 指在類的宣告中的一對花括號所形成的作用域。一個類的所有成員都在該類的作用域內。在類的作用域中,一個類的任何成員可以不受限制地訪問該類中的其他成員。而在類的作用域外,對該類的資料成員和成員函式的訪問則要受到一定的限制,有時甚至是不允許的,這與類成員的訪問屬性有關。

類成員的訪問屬性: 說明為公有的成員不但可以被類中的成員函式訪問,還可在類的外部,通過類的物件進行訪問;說明為私有的成員只能被類中的成員函式訪問,不能在類的外部,通過類的物件進行訪問。

一般來說,公有成員是類的對外介面,而私有成員是類的內部資料和內部實現,不希望外界訪問。