類中的共有結點
類是產生對象的模板,但每個由類建立的對象都有其隔離儲存區 (Isolated Storage)空間,所以對象的資料成員的值各不相同,但有時候需要讓類的所有對象在類的範圍內共用一些成員,這些成員不屬於任何對象,而是屬於類的,成為類中的共有結點。
為了實作類別的資料共用,就必須在類中聲明靜態成員。用於解決同一個類的不同對象之間的資料和函數的共用問題。使用待用資料成員可以節省記憶體佔用空間,因為其實所有對象共用的,所以多個對象的待用資料成員也只佔用一個待用資料成員的空間。
靜態成員有效期間為定義開始直到程式結束為止,所以必須對其進行初始化才能使用,的初始化不能在建構函式中進行,只能在類體外。
用一段程式例子看來感受 待用資料成員實現資料共用。
#include <iostream>#include<string.h>using namespace std;class CTest{ public: CTest(int a,int b,int c); void dispsum(); void disp(); private: int m_a; int m_b; int m_c; static int sum; //聲明待用資料成員sum};int CTest::sum=0; //初始化待用資料成員CTest::CTest(int a,int b,int c){ m_a=a; m_b=b; m_c=c; sum=sum+m_a+m_b+m_c; //更新待用資料成員sum的值}void CTest::disp(){ cout<<"num="<<m_a<<","<<m_b<<","<<m_c<<endl;}void CTest::dispsum(){ cout<<"sum="<<sum<<endl;}int main(){ CTest t1(5,5,10); //聲明對象t1 CTest t2(8,9,10); t1.disp(); t1.dispsum(); t2.disp(); t2.dispsum(); return 0;}
運行結果為:
num = 5,5,10sum = 47num = 8,9,10sum = 47
分析輸出結果,可以看出,sum的值在對象t1和對象t2輸出時,其值是相同的,這是因為在建立t1對象時,將t1對象的三個資料成員的值和sum求和後,賦值給了sum。然後在建立t2對象時,又將t2對象的三個資料成員的值和sum中的已有值進行求和,再賦值給了sum,於是sum中時儲存的最後修改的值。這也說明了對象t1和對象t2都是共用的類的待用資料成員sum。
靜態成員函數
靜態成員函數的實現中不能直接使用類中聲明的非待用資料成員,否則會產生編譯錯誤。
通過程式感受如何聲明、實現靜態成員函數並調用的:
#include <iostream>#include<string.h>using namespace std;class CDemo{ public: CDemo(int x); static void disp(); //聲明靜態成員函數 static void setNum(CDemo d); //聲明靜態成員函數 private: int n; //聲明一般資料成員 static int num; //聲明待用資料成員};int CDemo::num=0; //初始化待用資料成員CDemo::CDemo(int x) //建構函式{ n=x; num+=x;}void CDemo::disp() //靜態成員函數的實現,只能使用待用資料成員{ cout<<"num="<<num<<endl;}void CDemo::setNum(CDemo d){ num=d.n; //要使用類中的非靜態成員,只能通過參數把對象傳遞進來在使用。} //靜態成員函數通過使用同類對象來訪問非待用資料成員int main(){ CDemo demo1(10); CDemo::disp(); //直接調用待用資料成員函數 CDemo demo2(5); CDemo::setNum(demo2); //直接調用待用資料成員函數 CDemo::disp(); //直接調用待用資料成員函數 return 0;}
this指標
類的成員函數中隱藏著一個特別的指標:this指標。其是一個指向調用當前成員函數的對象的指標,不用聲明,不用初始化,不用對其賦值,但是它就是指向了調用成員函數的對象。
其實this指標是一個編譯系統隱含的指標,存在於類的每一個成員函數之中。在一個對象調用成員函數是,系統會自動現將對象的地址賦值給this指標(其類型為常類型故不能被修改),然後調用成員函數,在對象的成員函數中存取資料時,則都是隱含使用了this指標來操作對象的資料成員。像前面的在對象的成員函數中訪問對象的資料成員都是隱式的通過this指標來操作的,只是沒有顯示的表示出來
在對象的成員函數中,可用this來指向對象的成員,也使用*this來表示調用該成員函數的對象。
通過一個例子來看this指標的用法。
#include <iostream>#include<string.h>using namespace std;class CPoint{ public: CPoint(int x, int y); void copy(CPoint *point); //參數為對象指標 void disp(); private: int m_x; int m_y;};CPoint::CPoint(int x,int y){ m_x=x; m_y=y;}void CPoint::copy(CPoint *point){ if(this==point) //表示為同一對象,因為其地址相同 { return; //不複製 } *this=*point; //把當前對象的值變成形參所指對象的值(把形參point對象指標所指向的全部資料成員的值賦值給當前調用成員函數的對象的全部資料成員。)} void CPoint::disp(){ cout<<"m_x:"<<m_x<<";m_y"<<m_y<<endl;}int main(){ CPoint p1(10,10),p2(88,88); p1.copy(&p2); //把對象p2的地址即指標,傳遞給函數 p1.disp(); return 0;}
友元函數和友元類(破壞了類的封裝性和隱藏性,不推薦使用)
友元是一種定義在類的外部,在類體中聲明的普通函數。在類體中聲明時,前面需要加上關鍵字friend來和成員函數加以區別。
友元不是成員函數,但是友元能使普通函數直接存取類的私人資料,避免了類成員函數的頻繁調用,可以節約處理器開銷,提高程式效率。
友元有兩種形式:一種是友元函數,一種是友元類
友元函數的使用 的程式:
#include <iostream>#include<string.h>using namespace std;class CRect{ public : CRect(int new_heigh,int new_width); friend int getArea(CRect &r); //聲明由原函數,&的意思是引用這個對象 friend int getGrith(CRect &r);//聲明由原函數 private: int heigh; int width;};CRect::CRect(int new_heigh,int new_width) //::是範圍運算子{ heigh=new_heigh; width=new_width;}int getArea(CRect &r){ return r.heigh*r.width; //訪問類的私人成員}int getGrith(CRect &r){ return (r.heigh+r.width)*2;}int main(){ CRect rect(20,100); cout<<"矩形的面積為:"<<getArea(rect)<<endl; //調用友元函數 cout<<"矩形的周長為:"<<getGrith(rect)<<endl;}
友元不被成員存取控制符所限制,可以存取類中任何資料成員。
友元類:
即一個類作為另一個類的友元,這個類的所有成員函數都是另一個類的友元函數。通過程式感受友元類的聲明和使用:
#include <iostream>#include<string.h>using namespace std;class CApple{ friend class COrange; //聲明友元類為COrange public: CApple() { m_x=0; m_y=0; } CApple(int x,int y); void disp(); private: int m_x; int m_y;};CApple::CApple(int x,int y){ m_x=x; m_y=y;}void CApple::disp(){ cout<<m_x<<";"<<m_y<<endl;}class COrange //友元類COrange的聲明{ public: COrange(int x,int y); void disp(); private: CApple m_apple; //聲明資料成員為CApple的對象};COrange::COrange(int x,int y) { m_apple.m_x=x; //訪問對象m_apple的私人成員m_x;因為COrange類是CApple類的友元類,所以其可以修改CApple類的對象m_apple中的私人成員m_x的值。 m_apple.m_y=y;}void COrange::disp(){ cout<<m_apple.m_x<<";"<< m_apple.m_y<<endl;}int main(){ CApple apple(55,88); apple.disp(); COrange orange(10,100); orange.disp();}
多功能操作符
例子一:操作符的重載和應用(未成功,哪裡錯了??)
#include <iostream>#include<string.h>using namespace std;class CString{ public: CString(); CString(char *pStr); char *getStr(); CString operator+(CString &t); //重載操作符+ private: char str[128];};CString::CString(){ str[0]='\0';}CString::CString(char *pStr){ strcpy(str,pStr);}char *CString::getStr(){ return str;}CString CString::operator+(CString,&t) //操作符重載的實現{ CString tmp; strcat(str,t.str); //把當前對象的字串和參數的字元相加 strcat(tmp.str,str); return tmp; //返回臨時對象}int main(){ CString str1("this is"),str2("operator overload"),str3; str3=str1+str2; //調用重載後的操作符“+”,實現兩個CString類對象的相加 cout>>str3.getStr>>endl; return 0;}
srf同學幫忙修改後的~think
#include <iostream>#include<string.h>using namespace std;class CString{ public: CString(); CString(const char *pStr); char *getStr(); CString operator+(const CString &t); //重載操作符+ private: char *str;};CString::CString(){ str = new char[1]; str[0]='\0';}CString::CString(const char *pStr){ if(pStr == NULL) pStr = ""; int n = strlen(pStr); str = new char[n+1]; strcpy(str,pStr);}char * CString::getStr(){ return str;}CString CString::operator+(const CString &t) //操作符重載的實現{ char *pt; pt = new char[strlen(str)+strlen(t.str)+1]; strcpy(pt,str); strcat(pt,t.str); CString temp(pt); delete[]pt; return temp;}int main(){ CString str1("this is"); CString str2("operator overload"); CString str3; str3=str1+str2; //調用重載後的操作符“+”,實現兩個CString類對象的相加 cout<<str3.getStr()<<endl; return 0;}
賦值操作符的重載:
#include <iostream>#include<string.h>using namespace std;class CNum{ public: CNum(int a,int b,int c); CNum operator=(CNum &n); //重載賦值操作符 void print(); private: int x,y,z;};CNum::CNum(int a,int b,int c){ x=a; y=b; z=c;}CNum CNum::operator=(CNum &n) //賦值操作符重載的實現{ this->x=n.x; //把調用對象的x賦值為引用對象的x this->z=n.z; return *this;}void CNum::print() //顯示函數{ cout<<"x="<<x<<"y="<<y<<"z="<<z<<endl;}int main(){ CNum n1(15,100,20),n2(10,80,30);//建立對象n1,n2 n1.print(); n1=n2; //對象賦值 n2.print(); n1.print(); return 0;}
例子三:下標運算子的重載:(因為在C++的普通數組中,無法保證再給數組進行動態訪問或者賦值數時,不會發生下標越界的情況)
#include <iostream>#include<string.h>using namespace std;class CArray{ public: CArray(int len); ~CArray(); int getLength(); int & operator[](int index); //重載運算子,傳回值類型是參考型別,這樣其返回後才能被賦值。 private: int length; int *pArray;};CArray::CArray(int len){ length=len; pArray=new int[len]; //申請堆空間}CArray::~CArray(){ delete pArray; //釋放堆空間}int & CArray::operator[](int index) //重載的實現{ static int n=0; if(index>=0&&index<length) //判斷是否越界 { return pArray[index]; }else { cout<<"下標越界"<<endl; //輸出錯誤資訊 } return n;}int CArray::getLength() //獲得長度{ return length;}int main(){ CArray array(10); int i=0; for(i=0;i<array.getLength();i++) //根據獲得的長度進行判斷,就不越界 { array[i]=i; //賦值給數組的每個元素 } for(i=0;i<array.getLength();i++) { cout<<array[i]<<","; } cout<<endl; return 0;}
檔案輸入輸出對象的使用
檔案處理函數放在了fstream.h標頭檔中,要調用檔案處理函數時,把fstream.h檔案插入到當前代碼中。在C++中的檔案讀寫操作使用fstream對象進行,其包含了若干個檔案操作的成員函數。
使用檔案對象開啟和關閉檔案
#include <iostream>#include <fstream>using namespace std;int main(){ ifstream fs; //建立檔案對象 fs.open("d:/11.txt",ios::in|ios::out);//調用檔案對象的成員函數 if(fs.is_open()) //調用成員函數來判斷是否開啟成功 { fs.close(); //關閉檔案 } return 0;}
寫入資料到檔案中:
#include <iostream>#include <fstream>using namespace std;int main(){ fstream fs; //建立檔案對象 fs.open("d:/11.txt",ios::out);//調用檔案對象的成員函數 if(fs.is_open()) //調用成員函數來判斷是否開啟成功 { fs<<"this is test\n"; //輸入資料到檔案 fs<<"good bye\n"; fs.close(); //關閉檔案 } return 0;}
讀取檔案資料:
#include <iostream>#include <fstream>using namespace std;int main(){ fstream fs; //建立檔案對象 char buffer[26]; fs.open("d:/11.txt",ios::in|ios::out);//調用檔案對象的成員函數 if(fs.is_open()) //調用成員函數來判斷是否開啟成功 { while(!fs.eof()) //判斷檔案是否是檔案尾部 { fs.getline(buffer,100); //讀取資料 通過檔案對象把檔案的資料讀入到buffer中 cout<<buffer<<endl; //輸出資料 } fs.close(); //關閉檔案 } return 0;}