官术网_书友最值得收藏!

2.2 數據共享和成員特性

類的重要特性是使數據封裝與隱藏,但同時也給數據共享及外部訪問帶來了不便。為此, C++提供了靜態成員和友元機制來解決這些問題,但同時也要注意它們的副作用。除靜態(static)成員之外,C++的成員還可以用const等來修飾,且成員函數中還隱藏一個特殊的this指針。

2.2.1 靜態成員

以往實現數據共享的做法是設置全局變量或全局對象,但全局變量或全局對象是有許多局限性的:一來嚴重破壞類的封裝性,二來全局變量或全局對象的濫用會導致程序的混亂,一旦程序變大,維護量就急劇上升。

靜態成員能較好地解決上述問題,首先靜態成員是類中的成員,是類的一部分,在類外不可訪問,從而起到保護作用。其次,靜態成員有靜態數據成員和靜態成員函數之分。靜態數據成員與靜態變量相似,具有靜態生存期,是在類中聲明的全局數據成員,能被同一個類的所有對象所共享。而公有靜態成員函數不僅可以通過類對象來訪問,還可通過“類名::靜態成員函數”的形式在程序中直接調用。

1.靜態數據成員

使用靜態數據成員可以節省內存,因為它是所有對象所公有的,因此,對多個對象來說,靜態數據成員只存儲一處,供所有對象共享。靜態數據成員的值是可以修改的,但它對每個對象都是一樣的。

與靜態變量相似,靜態數據成員是靜態存儲(static)的,但定義一個靜態數據成員與一般靜態變量不一樣,它必須按下列兩步進行:

(1)在類中使用關鍵字static聲明靜態數據成員。在類中聲明靜態數據成員,僅僅是說明了靜態數據成員是類中的成員這個關系,即便用該類定義對象時,該靜態數據成員也不會分配內存空間。因此可以說,類中聲明的靜態數據成員是一種形式上的虛的數據成員。靜態數據成員的實際定義是由下一步來完成的。

(2)在類外為靜態數據成員分配內存空間并初始化。類中數據成員的內存空間是在對象定義時來分配的,但靜態數據成員的內存空間為所有該類對象所共享,只能分配一次,因而不能通過定義類對象的方式來分配,必須在類的外部作實際定義才能為所有對象共享,其定義格式如下:

<數據類型><類名>::<靜態數據成員名>=<值>

可見,在類外初始化的靜態數據成員與全局變量初始化格式相似,只是必須指明它所屬的類。由于靜態數據成員的靜態屬性static已在類中聲明,因此在類外不可再指定static。

【例Ex_StaticData】 靜態數據成員的使用

#include <iostream.h>
class CSum
{
public:
    CSum(int a=0,int b=0)                 //A
    {    nSum+=a+b;       }
    int getSum()
    {   return nSum;       }
    void setSum(int sum)
    {   nSum=sum;         }
private:
    static int nSum;                      // 聲明靜態數據成員
};
int CSum::nSum=0;                        // 靜態數據成員的實際定義和初始化
int  main()
{
    CSum one(10, 2), two;
    cout<<"one: sum = "<<one.getSum()<<endl;
    cout<<"two: sum = "<<two.getSum()<<endl;
    two.setSum(5);
    cout<<"one: sum = "<<one.getSum()<<endl;
    cout<<"two: sum = "<<two.getSum()<<endl;
    return 0;
}

分析:

(1)A中,由于使用了默認參數,因而使得默認構造函數和重載構造函數定義成一個構造函數。這種程序方法在實際應用時要小心使用。

(2)程序中,類CSum中的私有數據成員nSum被聲明成靜態的,由于類中聲明的nSum是虛的,因此它必須在類體外進行實際定義。若不指定初值,則默認為0。

(3)main函數中,對象one初始化后,nSum值變為12。對象two由于調用的是(a=0, b=0)的默認構造函數,故nSum的值沒有變化,仍然是12(注意構造函數體的語句“nSum += a+b;”中的“+=”不是“=”)。因此,main函數中前面兩條輸出語句的結果都是輸出12。當執行“two.setSum(5);”后,nSum值被設為5。由于nSum為所有對象所共享,也就是說,nSum是所有對象的公共成員,因此對象one中的nSum值也是5。

程序運行結果如下:

one: sum = 12

two: sum = 12

one: sum = 5

two: sum = 5

需要說明的是:

(1)由于靜態數據成員在類中所作的聲明僅僅聲明該成員是屬于哪個類的,它是形式上的虛的成員,還必須在類的外部作實際定義才能被所有對象共享,正因為如此,靜態數據成員的實際定義和初始化本身不受public、private和protected等訪問屬性的限制。

(2)靜態數據成員可看成類中聲明、類外定義的靜態全局變量,因此它具有靜態生存期,在程序中從實際定義時開始產生,到程序結束時消失。也就是說,靜態數據成員的內存空間不會隨對象的產生而分配,也不會隨對象的消失而釋放。當然,靜態數據成員的內存空間同樣不能在類的構造函數中創建或是在析構函數中釋放。

(3)靜態數據成員是類中的成員,它的訪問屬性同普通數據成員一樣,可以為public、private和protected。當靜態數據成員為public時,則在類外對該成員的訪問和引用可有兩種方式,一是通過對象來引用,二是直接引用。當直接引用時,應使用下列格式:

<類名>::<靜態成員名>

例如,有:

class CSum
{
    //…
public:
    static int nSum;                     // 聲明公有型靜態數據成員
};
int CSum::nSum=0;                       // 靜態數據成員的實際定義和初始化

則在main函數中可有下列引用:

int  main()
{
     CSum one;
     one.nSum=10;                        // 通過對象來引用
     //…
     CSum::nSum=12;                      // 直接引用
     cout<<one.nSum<<endl;               // 輸出12
     return 0;
}

代碼中,引用公有型靜態數據成員nSum的兩種方式都是合法的,也是等價的。

2.靜態成員函數

靜態成員函數和靜態數據成員一樣,它們都屬于類的靜態成員,但它們都不專屬于某個對象的成員,而是所有對象所共享的成員。因此,對于公有型(public)靜態成員來說,除可用對象來引用外,還可通過“類名::成員”直接來引用。

在類中,靜態數據成員可以被成員函數引用,也可以被靜態成員函數所引用。但反過來,靜態成員函數卻不能直接引用類中說明的非靜態成員。假如靜態成員函數可以引用類中的非靜態成員,例如:

class CSum
{
public:
    static void ChangeData(int data)
    {
        nSum=data;                  // 錯誤:引用類中的非靜態成員
    }
public:
    int nSum;
};則當執行語句:
CSum::ChangeData(5);                  // 合法的靜態成員引用

時必然會出現編譯錯誤,這是因為此時CSum類的任何對象都還沒有創建,nSum數據成員根本就不存在。即使是創建了CSum類對象,此時這種形式的靜態成員函數調用根本無法確定函數中所引用的nSum是屬于哪個對象的,因此靜態成員函數只能引用靜態數據成員,因為它們都是獨立于對象實例之外而為對象所共享的成員。

下面來看一個示例,它用靜態成員來實現數據插入、輸出和排序操作。

【例Ex_StaticMember】 靜態成員的使用

#include <iostream.h>
class CData
{
public:
    static void Add(int a)               // 添加數據a
    {
        if ( pCur >= data + 20 )
            cout<<"內存空間不足,無法添加!"<<endl;
        else
        {
            *pCur=a; pCur++;
        }
    }
    static void Print(void);
    static void Sort(void);
private:
    static int data[20];                // 聲明靜態內存空間
    static int*pCur;                    // 聲明靜態指針成員
};
int CData::data[20];                    // 實際定義,默認的初值為0
int*CData::pCur=data;                   // 實際定義,設初值為data數組的首地址
void CData::Print(void)                 // 類外定義的靜態成員函數
{
    for (int i=0; i<(pCur-data); i++)
        cout<<data[i]<<", ";
    cout<<endl;
}
void CData::Sort(void)                  // 類外定義的靜態成員函數
{
    int n = pCur - data;
    for (int i=0; i< n -1; i++)
        for (int j=i+1; j<n; j++)
            if ( data[i] > data[j] )
            {
                int temp=data[i];      data[i]=data[j];     data[j]=temp;
            }
}
int  main()
{
    CData::Add(20);    CData::Add(40);    CData::Add(-50);
    CData::Add(7);     CData::Add(13);
    CData::Print();
    CData::Sort();
    CData::Print();
    return 0;
}

同普通成員函數,類中的靜態成員函數也可在類中聲明,而在類外實現。由于類CData中所有的成員都聲明成了靜態的,因此main函數直接通過“類名::成員”的形式來引用其public成員,通過靜態成員函數Add在靜態數組成員data中設置并添加數組,每添加一次,靜態指針成員pCur的指向就向下移動一次,使其指向下一個數組元素的內存空間。靜態成員函數Print和Sort分別將data數組中的已有數組輸出和排序。

程序運行結果如下:

20, 40, -50, 7, 13,

-50, 7, 13, 20, 40,

可見,若將相同類別的操作均用公有型靜態成員函數來實現,則無須通過對象就可引用類中的成員,此時的類成了一種工具集,這在強調程序算法的場合下得到了廣泛應用。

需要強調的是:

(1)靜態成員中的“靜態(static)”與普通靜態變量和靜態函數中“靜態”的含義是不一樣的。普通靜態變量中的“靜態”是使用靜態存儲內存空間,而類中的靜態數據成員的“靜態”是對象數據共享的聲明,并非具有實際意義的靜態存儲內存空間。普通靜態函數中的“靜態”是表示本程序文件的內部函數,而類中的靜態成員函數的“靜態”表示該成員函數僅能訪問靜態數據成員,是為所有該類對象共享的聲明方式。

(2)類的靜態數據成員的內存開辟和釋放只能通過靜態成員函數來實現,而不能通過類的構造函數和析構函數來完成。C++中也沒有靜態構造函數和靜態析構函數。

靜態成員為對象的數據和操作共享提供了方便,但同時也帶來了副作用。例如,若在一個對象中,它的某個操作修改了某個靜態數據成員,然而這一修改造成了所有該類對象的該靜態數據成員進行了更新,便會給程序帶來許多潛在的危險,并且不同對象的多次修改還會使靜態數據成員的值被任意更新,從而導致程序的混亂,類的封裝性也被嚴重破壞。

那么有沒有辦法允許在類外只對某個對象的數據成員進行操作呢?于是,友元(friend)的機制便產生了,并且友元還能訪問類中private和protected成員。

1.友元概述

通常,類的私有型(private)數據成員和保護型(protected)數據成員只能在類中由該類的成員函數來訪問,類的對象及外部函數只能訪問類的公有型(public)成員函數,類的私有和保護型數據成員只能通過類的成員函數來訪問,如圖2.1(a)所示。但是,如果在類中用friend關鍵字聲明一個函數,且該函數的形參中還有該類的對象形參,這個函數便可通過形參對象或通過在函數體中定義該類對象來訪問該類的任何私有和保護型數據成員,如圖2.1(b)所示。這就好比一個人的秘密可以讓“密友”知道一樣,用friend聲明的這個函數就稱為這個類的友元函數

2.2.2 友元

除友元函數外,友元還可以是類,即一個類可以作為另一個類的友元,稱為友元類。例如,當B類作為A類的友元時,就意味著要在B類中通過A類對象來訪問A類中的所有成員。

可見,采用友元機制,通過類對象可以訪問或引用類中的所有成員。這樣,即使通過友元來修改數據成員,修改的也僅僅是某個對象的數據成員,從而既保證了類的封裝性,也為外部訪問類的私有和保護型成員提供方便。

圖2.1 外部函數和友元對類成員的訪問關系

2.友元函數

友元函數可分為友元外部函數和友元成員函數。當一個函數f是A類的友元,且f還是另一個類B的成員函數時,則這樣的友元稱為友元成員函數;若f不屬于任何類的成員,則這樣的友元稱為友元外部函數。通常將友元外部函數直接簡稱為友元函數

友元函數在類中定義的格式如下:

friend <函數類型> <函數名>(形參表)
{…}

從格式中可以看出,友元函數和類的成員函數定義格式基本一樣,只是友元函數前面用一個關鍵字friend來修飾。由于友元函數與類的關系是一種“友好(friendship)”關系,因此友元函數不屬于類中的成員函數,它是在類中聲明的一個外部函數。需要說明的是:

(1)友元函數的定義可在類中進行,也可將友元函數在類中聲明,而將其實現在類外定義。但在類外定義時,不能像成員函數那樣指明它所屬的類。

(2)由于友元函數是一個外部函數,因此它對類中的成員訪問只能通過類對象來進行,而不能直接去訪問。這里的對象可以通過形參來指定,也可在友元函數中進行定義。

(3)由于友元函數是類中聲明的外部函數,因而它跟成員的訪問權限private、protected和public沒有任何關系,因此它的聲明可以出現在類中的任何部分,包括在private和public部分。但為了增強程序的可讀性,常將友元函數聲明在類體的開頭或最后。

(4)由于友元函數不是類的成員,因此它在調用時不能指定其所屬的類,更不能通過對象來引用友元函數。

(5)大多數外部函數對類中的數據操作是采用形參對象的方式,通過對象的“引用”傳遞,達到修改對象數據的目的。對于友元函數,也應該采用這種方式,只是友元函數還能修改對象的私有和保護型數據成員。

下面來舉一個例子,它是通過友元函數將一個點(Cpoint)的位置發生偏移。

【例Ex_FriendFun】 使用友元函數

#include <iostream.h>
class CPoint
{
    friend CPoint Inflate(CPoint&pt,int nOffset);  // 聲明一個友元函數
public:
    CPoint( int x = 0, int y = 0 )
    {
        xPos=x;      yPos=y;
    }
    void  Print()
    {
        cout << "Point(" << xPos << ", " << yPos << ")"<< endl;
    }
private:
    int xPos,  yPos;
};
CPoint Inflate(CPoint&pt,int nOffset)        // 友元函數的定義
{
    pt.xPos+=nOffset;                        // 直接改變私有數據成員xPos和yPos
    pt.yPos += nOffset;
    return  pt;
}
int  main()
{
    CPoint pt( 10, 20 );
    pt.Print();
    Inflate(pt,3);                           // 直接調用友元函數
    pt.Print();
    return 0;
}

在類CPoint中,Inflate是在類中聲明、類外定義的友元函數。它有兩個形參:一是引用形參對象pt,二是int形參變量nOffset。在友元函數中,由于對象的所有數據成員可以直接訪問,因此可直接將形參對象pt的私有數據成員直接加上nOffset指定的偏移量。由于Inflate指定的pt對象是引用傳遞,因此對pt內容的修改也就是對實參對象內容的修改。

程序運行結果如下:

Point(10, 20)

Point(13, 23)

3.友元成員函數

友元成員函數在類中定義的格式如下:

friend <函數類型> <類名>::<函數名>(形參表)
{…}

由于友元成員函數還是另一個類的成員函數,因此這里的類名是指它作為成員所在的類名。與成員函數一樣,友元成員函數的定義既可在類中進行,也可將友元成員函數在類中聲明,而將其實現在類外定義。但當在類外定義時,應像成員函數那樣指明它所屬的類。

【例Ex_FriendMemFun】 使用友元成員函數

#include <iostream.h>
class CRect;                                     // 聲明類名CRect,以便可以被后面引用
class CPoint
{
public:
    void Inflate(CRect&rc,int nOffset);           // 成員函數
    CPoint( int x = 0, int y = 0 )
    {
        xPos=x;      yPos=y;
    }
    void  Print()
    {
        cout<<"Point("<<xPos<<", "<<yPos<<")"<<endl;
    }
private:
    int xPos,  yPos;
};
class CRect
{
    friend void CPoint::Inflate(CRect &rc, int nOffset);
public:
    CRect(int x1=0, int y1=0, int x2=0, int y2=0)
    {
        xLeft=x1;    xRight=x2;
        yTop=y1;     yBottom=y2;
    }
    void  Print()
    {
        cout<<"Rect("<<xLeft<<", "<<yTop<<", "<<xRight<<", "<<yBottom<<")"<< endl;
    }
private:
    int xLeft, yTop, xRight, yBottom;
};
void CPoint::Inflate(CRect&rc,int nOffset)      // 友元函數的定義
{
    xPos+=nOffset;   yPos+=nOffset;             // 直接改變自己類中的私有數據成員
    // 訪問CRect類的私有成員
    rc.xLeft+=nOffset;      rc.xRight+=nOffset;
    rc.yTop+=nOffset;       rc.yBottom+=nOffset;
}
int main()
{
    CPoint pt( 10, 20 );
    CRect rc( 0, 0, 100, 80 );
    pt.Print();     rc.Print();
    pt.Inflate(rc,3);                           // 調用友元函數
    pt.Print();     rc.Print();
    return 0;
}

在類CRect中聲明了一個友元函數Inflate,由于它還是類CPoint的成員函數,因此Inflate既可以直接訪問CPoint的所有成員,也可以通過CRect類對象訪問類CRect中的所有成員。由于在類CPoint中的Inflate函數的形參含有CRect對象,而此時CRect類還沒有定義,因此需要在類CPoint前先作CRect類的聲明,以便后面能使用CRect數據類型。

程序運行結果如下:

Point(10, 20)

Rect(0, 0, 100, 80)

Point(13, 23)

Rect(3, 3, 103, 83)

4.友元類

除一個類的成員函數可以聲明成另一個類的友元外,也可以將一個類聲明成另一個類的友元,稱為友元類。當一個類作為另一個類的友元時,就意味著這個類的所有成員函數都是另一個類的友元成員函數。友元類的聲明比較簡單,其格式如下:

friend class <類名>;

下面來看一個例子。

【例Ex_ClassFriend】 使用友元類

#include <iostream.h>
class CPoint
{
    friend class COther;                      // 聲明友元類
public:
    CPoint( int x = 0, int y = 0 )
    {
        xPos=x;       yPos=y;
    }
    void  Print()
    {
        cout<<"Point("<<xPos<<", "<<yPos<<")"<<endl;
    }
private:
    int xPos,  yPos;
    void Inflate(int nOffset)
    {   xPos+=nOffset;   yPos+=nOffset;     // 直接改變自己類中的私有數據成員
    }
};
class COther
{
public:
    COther(int a = 0, int b = 0)
    {
        pt.xPos=a;    pt.yPos=b;          // 通過對象訪問類CPoint的私有數據成員
    }
    void Display(void)
    {
        pt.Inflate(10);                   // 通過對象訪問類CPoint的私有成員函數
        pt.Print();
    }
private:
    CPoint pt;
};
int main()
{
    COther one(12,18);
    one.Display();
    return 0;
}

在類CPoint的定義中,類COther被聲明成CPoint的友元類。這樣,在類COther中,可通過CPoint對象pt訪問類CPoint的所有成員。

程序運行結果如下:

Point(22, 28)

總之,關于友元有下面的一些說明:

(1)友元關系反映了程序中類與類之間、外部函數和類之間、成員函數和另一個類等之間的關系,這個關系是單向的,即當在CPoint中聲明COther是CPoint的友元類時,只能在COther類中通過CPoint對象訪問CPoint類的所有成員,而在CPoint類中無法訪問COther類的私有和保護型成員。

(2)一個類中的友元并非是該類的成員,由于“friend”關系,因而友元只能通過對象來訪問聲明友元所在類的成員。而靜態成員是類的一個成員,它本身具有不同的訪問屬性,只是對于公有靜態成員來說,它可以有對象訪問和“類名::靜態成員”兩種等價的訪問方式。

(3)與友元函數相比,靜態成員函數只是修改類的靜態數據成員,而對于友元來說,由于通過對象可以修改聲明友元所在類的所有數據成員,因而友元函數比靜態成員函數更加危險,而且友元類會使這種危險擴大。因此,在類程序設計中,靜態成員和友元一定要慎用!

2.2.3 常類型

常類型是指使用類型修飾符const說明的類型,常類型的變量或對象的值是不能被更新的。因此,定義或說明常類型時必須進行初始化。

1.常對象

常對象是指對象常量,定義格式如下:

<類名> const <對象名>

定義常對象時,修飾符const可以放在類名后面,也可以放在類名前面。例如:

class COne
{
public:
    COne(int a, int b) { x = a; y = b; }
    //…
private:
    int x, y;
};
const COne a(3,4);
COne const b(5,6);

其中,a和b都是Cone的對象常量,初始化后就不能再被更新。

2.常指針和常引用

常指針也是使用關鍵字const來修飾的。但需要說明的是,const的位置不同,其含義也不同,它有3種形式。

第1種形式是將const放在指針變量的類型之前,表示聲明一個指向常量的指針。此時,在程序中不能通過指針來改變它所指向的數據值,但可以改變指針本身的值。例如:

int a = 1, b = 2;
const int*p1=&a;              // 聲明指向int型常量的指針p1, 指針地址為a的地址
*p1=2;                        // 錯誤,不能更改指針所指向的數據值
p1=&b;                        // 正確,指向常量的指針本身的值是可以改變的

需要說明的是,用這種形式定義的常量指針,在聲明時可以賦初值,也可以不賦初值。

第2種形式是將const放在指針定義語句的指針名前,表示指針本身是一個常量,稱為指針常量或常指針。因此,不能改變這種指針變量的值,但可以改變指針變量所指向的數據值。例如:

int a = 1, b = 2;
int*const p1=&a;              // 聲明指向int型常量的指針p1, 指針地址為a的地址
int*const p2;                 // 錯誤,在聲明指針常量時,必須初始化
*p1=2;                        // 正確,指針所指向的數據值可以改變
p1=&b;                        // 錯誤,指針常量本身的值是不可改變的

第3種形式是將const在上述兩個地方都加,表示聲明一個指向常量的指針常量,指針本身的值不可改變,而且它所指向的數據的值也不能通過指針改變。例如:

int a = 1, b = 2;
const int * const pp = &a;
*pp=2;                      // 錯誤
pp=&b;                      // 錯誤

需要說明的是,用第2種和第3種形式定義的指針常量,在聲明時必須賦初值。

使用const修飾符也可用來聲明引用,被聲明的引用為常引用,該引用所引用的對象不能被更新。其定義格式如下:

const <類型說明符> & <引用名>

例如:

const double & v;

在實際應用中,常指針和常引用往往用來當做函數的形參,這樣的參數稱為常參數。使用常參數則表明該函數不會更新某個參數所指向或所引用的對象,這樣,在參數傳遞過程中就不需要執行拷貝構造函數,這將會改善程序的運行效率。

【例Ex_ConstPara】有常參數的函數傳遞

#include <iostream.h>
class COne
{
public:
    void print(const int*p,int n)    // 使用常參數
    {
        cout<<"{"<<*p;
        for (int i = 1; i<n; i++)
            cout<<", "<<*(p+i);
        cout<<"}"<<endl;
    }
};
int  main()
{
    int array[6] = {1, 2, 3, 4, 5, 6};
    COne one;
    one.print(array, 6);
    return 0;
}

程序運行結果如下:

{1, 2, 3, 4, 5, 6}

3.常成員函數

使用const關鍵字進行聲明的成員函數,稱為常成員函數。只有常成員函數才有資格操作常量或常對象,沒有使用const關鍵字說明的成員函數不能用來操作常對象。常成員函數說明格式如下:

<類型說明符> <函數名> (<參數表>) const;

其中,const是加在函數說明后面的類型修飾符,它是函數類型的一個組成部分,因此,在函數實現部分也要帶const關鍵字。

【例Ex_ConstFunc】 常成員函數的使用

#include <iostream.h>
class COne
{
public:
    COne(int a, int b)
    {
        x = a; y = b;
    }
    void print();
    void print()const;                // 聲明常成員函數
private:
    int x, y;
};
void COne::print()
{
    cout<<x<<", "<<y<<endl;
}
void COne::print() const
{
    cout<<"使用常成員函數:"<<x<<", "<<y<<endl;
}
int  main()
{
    COne one(5, 4);
    one.print();
    const COne two(20, 52);
    two.print();
return 0;
}

程序運行結果如下:

5, 4

使用常成員函數:20, 52

程序中,類COne聲明了兩個重載成員函數,一個帶const,另一個不帶。語句“one.print();”調用成員函數“void print();”,而“two.print();”調用常成員函數“void print() const;”。

4.常數據成員

類型修飾符const不僅可以說明成員函數,也可以說明數據成員。由于const類型對象必須被初始化,并且不能更新,因此,當類中聲明了const數據成員時,只能通過成員初始化列表的方式來生成構造函數對數據成員進行初始化。

【例Ex_ConstData】 常數據成員的使用

#include <iostream.h>
class COne
{
public:
    COne(int a)
        :x(a),r(x)                // 常數據成員的初始化
    {   }
    void print();
    const int&r;                  // 引用類型的常數據成員
private:
    const int x;                  // 常數據成員
    static const int y;           // 靜態常數據成員
};
const int COne::y=10;             // 靜態數據成員的初始化
void COne::print()
{
    cout<<"x = "<<x<<", y = "<<y<<", r = "<<r<<endl;
}
int  main()
{
    COne one(100);
    one.print();
    return 0;
}

程序運行結果如下:

x = 100, y = 10, r = 100

2.2.4 this指針

this指針是一個僅能被類的非靜態成員函數所訪問的特殊指針。當一個對象調用成員函數時,編譯器先將對象的地址賦給this指針,然后調用成員函數。例如,當下列成員函數調用時:

one.copy(two);

它實際上被解釋成:

copy( &one, two);

只不過是&one參數被隱藏了。需要說明的是,通過*this可以判斷是哪個對象來調用該成員函數或重新指定對象。

【例Ex_This】 this指針的使用

#include <iostream.h>
class COne
{
public:
    COne()
    {   x=y=0; }
    COne(int a, int b)
    {
    x = a; y = b;
    }
    void copy(COne&a);         // 對象引用做函數參數
    void print()
    {
        cout<<x<<" , "<<y<<endl;
    }
private:
    int x, y;
};
void COne::copy(COne &a)
{
    if (this == &a) return;
    *this = a;
}
int  main()
{
    COne one, two(3, 4);
    one.print();
    one.copy(two);
    one.print();
    return 0;
}

程序運行結果如下:

0 , 0

3 , 4

程序中,使用this指針的函數是copy,它在copy函數中出現了2次。“if(this == &a)”中的this是操作該成員函數的對象的地址,從main函數中的“one.copy(two);”可以看出這個對象就是one。copy函數中的語句

*this = a;

是將形參a(對象的引用)賦給操作該成員函數的對象。在本例中,就是將對象two賦給對象one。因此,main函數中最后的語句“one.print();”實際上就是“two.print();”。

事實上,若成員函數的形參名與該類的成員變量名同名,則必須用this指針來顯式區分,例如:

class CPoint
{
public:
    CPoint( int x = 0, int y = 0)
    {
        this->x=x;    this->y=y;
    }
    void Offset(int x, int y)
    {
        (*this).x+=x;   (*this).y+=y;
    }
    void Print() const
    {
        cout<<"Point("<<x<<", "<<y<<")"<<endl;
    }
private:
    int x, y;
};

類CPoint中的私有數據成員x、y和構造函數、Offset成員函數的形參同名,正是因為成員函數體中使用了this指針,從而使函數中的賦值語句合法有效,且含義明確。否則,如果沒有this指針,則構造函數中的賦值語句就變為了“x=x; y=y;”,顯然是不合法的。

需要說明的是,對于靜態成員函數來說,由于它是為所有對象所共享,因此在靜態成員函數中使用this指針將無法確定this的具體指向。所以,在靜態成員函數中是不能使用this指針的。

主站蜘蛛池模板: 宣武区| 北川| 浠水县| 诏安县| 东丰县| 穆棱市| 洪雅县| 麻栗坡县| 商南县| 宜良县| 玉树县| 元谋县| 连州市| 抚松县| 雅安市| 巴东县| 浑源县| 县级市| 昌邑市| 焉耆| 宝丰县| 台南县| 弋阳县| 永平县| 社会| 上蔡县| 东乌| 寻乌县| 华阴市| 德化县| 安化县| 西乌| 股票| 开原市| 百色市| 苍溪县| 北川| 堆龙德庆县| 南开区| 维西| 大城县|