C++ 與“類”有關的注意事項總結(五):指向類成員的指標

來源:互聯網
上載者:User

     假定 Screen類定義了四個新成員函數——forward() back() up()和down() 它們分別向右、向左、向上和向下移動游標。首先,我們在類體中聲明這些新的成員函數 :

class Screen {
public:
inline Screen& forward();
inline Screen& back();
inline Screen& end();
inline Screen& up();
inline Screen& down();
// 其他成員函數同前
private:
inline int row();
// 其他私人成員同前
};

 

    Screen類的使用者要求一個函數 repeat(),它執行使用者指定的操作 n 次,它的非通用的實現如下: 

Screen &repeat( char op, int times )
{
switch( op ) {
case DOWN: // 調用 Screen::down() n 次
  break;
case UP: // 調用 Screen::up() n 次
  break;
// ...
}
}

 

     雖然這種實現能夠工作,但是它有許多缺點 一個問題是,它依賴於 Screen的成員函數保持不變,每次增加或刪減一個成員函數,都必須更新 repeat(); 第二個問題是它的大小,由於必須測試每個可能的成員函數,所以repeat()的完整列表非常大而且不必要地複雜 。
     替換的辦法是一種更通用的實現。用Screen成員函數的指標類型的參數取代 OP,repeat() 不需要再確定使用者期望什麼樣的操作,整個switch 語句也可以被去掉。

 

一、類成員的類型

 

     函數指標不能被賦值為成員函數的地址,即使傳回型別和參數表完全符合。例如 ,下面的 pfi 是一個函數指標,該函數沒有參數,傳回型別為 int: 

int (*pfi) ();

     給出兩個全域函數 HeightIs()和 WidthIs() :

int HeightIs();
int WidthIs();

     把 HeightIs()和WidthIs()中的任何一個或兩個賦值給指標 pfi 都是合法且正確的: 

pfi = HeightIs;
pfi = WidthIs;

     類 Screen也定義了兩個訪問函數——height()和 width()——它們也沒有參數,傳回型別也 是 int :

inline int Screen::height() { return _height; }
inline int Screen::width() { return _width; }

     但是把 height()或 width()任何一個賦給指標 pfi 都是類型違例,都將導致編譯時間刻錯誤:

 
// 非法賦值: 類型違例
pfi = &Screen::height;

     為什麼會出現類型違例呢?成員函數有一個非成員函數不具有的屬性——它的類 (its class), 指向成員函數的指標必須與向其賦值的函數類型匹配,不是兩個而是三個方面都要匹配 :(1) 參數的類型和個數 (2) 傳回型別 (3) 它所屬的類類型 。

      成員函數指標首先必須被綁定在一個對象或者一個指標上,才能得到被調用對象的 this指標,然後才調用指標所指的成員函數。雖然普通函數指標和成員函數指標都被稱作指標,但是它們是不同的事物。 

     成員函數指標的聲明要求擴充的文法,它要考慮類的類型,對指向類資料成員的指標也是這樣。考慮 Screen類的成員 height 的類型,它的完整類型是“ short 型的 Screen類的成 員“,指向_height 的指標的完整類型是 ”指向short 型的 Screen類的成員“的指標,這可以寫為 :

short Screen::*
 
     指向 short 型的 Screen類的成員的指標的定義如下 :

 

short Screen::*ps_Screen;
 
    ps_Screen可以用_height 的地址初始化,如下:

 
short Screen::*ps_Screen = &Screen::_height;
 
     類似地,它也可以用_width的地址賦值 如下 

ps_Screen = &Screen::_width;

 

     定義一個成員函數指標需要指定函數傳回型別、參數表和類,例如,指向Screen成員函數並且能夠引用成員函數 height()和 width()的指標類型如下:

 
int (Screen::*) ()

     這種類型指定了一個指向類 Screen的成員函數的指標,它沒有參數,傳回值類型為int 。指向成員函數的指標可被聲明、初始化及賦值如下:

 
// 所有指向類成員的指標都可以用 0 賦值
int (Screen::*pmf1)() = 0;
int (Screen::*pmf2)() = &Screen::height;
 
pmf1 = pmf2;
pmf2 = &Screen::width;

     使用 typedef可以使成員指標的文法更易讀,例如,下面的類型定義:

 
Screen& ( Screen::* ) ()

     也就是指向 Screen類成員函數的一個指標,該函數沒有參數,返回Screen類對象的引用。它可以被下列 typedef定義 Action所取代:

 
typedef Screen& (Screen::*Action)();
 
Action default = &Screen::home;
Action next = &Screen::forward;

     指向成員函數類型的指標可以被用來聲明函數參數和函數傳回型別 ,我們也可以為成員函數類型的參數指定預設實參,例如:

 

Screen& action( Screen&, Action );

    action()被聲明為有兩個參數 ,一個 Screen類對象的引用 和一個指向類 Screen的成員函數的指標,該函數沒有參數,返回Screen類對象的引用。 action()可以以下列任意方式被調用:

 
Screen myScreen;
typedef Screen& (Screen::*Action)();
Action default = &Screen::home;
 
extern Screen& action( Screen&, Action = &Screen::display );
 
void ff()
{

action( myScreen );
action( myScreen, default );
action( myScreen, &Screen::end );
}

 

二、使用指向類成員的指標

 

     類成員的指標必須總是通過特定的對象或指向該類類型的對象的指標來訪問 ,我們通過使用兩個指向成員操作符的指標(針對類對象和引用的.* 以及針對指向類對象的指標的->*)來做到這一點,例如,如下所示, 我們通過成員函數的指標調用成員函數:

 
int (Screen::*pmfi)() = &Screen::height;
Screen& (Screen::*pmfS)( const Screen& ) = &Screen::copy;
 
Screen myScreen, *bufScreen;
 
// 直接調用成員函數
if ( myScreen.height() == bufScreen->height() )
bufScreen->copy( myScreen );
 
// 通過成員指標的等價調用
if ( (myScreen.*pmfi)() == (bufScreen->*pmfi)() )
(bufScreen->*pmfS)( myScreen );

 

     如下調用:

 
(myScreen.*pmfi)()
(bufScreen->*pmfi)()

    要求有括弧 因為叫用作業符——()——的優先順序高於成員操作符指標的優先順序,沒有括弧 

myScreen.*pmfi()
     將會被解釋為 
myScreen.*(pmfi())

     它會先調用函數 pmfi() 把它的傳回值與成員對象操作符的指標 .* 綁定,當然 pmfi的類型不支援這種用法,會產生一個編譯時間刻錯誤。 

 

     類似地 指向資料成員的指標可以按下列方式被訪問:

typedef short Screen::*ps_Screen;
 
Screen myScreen, *tmpScreen = new Screen( 10, 10 );
 
ps_Screen pH = &Screen::_height;
ps_Screen pW = &Screen::_width;
 
tmpScreen->*pH = myScreen.*pH;
tmpScreen->*pW = myScreen.*pW;

 

     下面是在開始時討論的成員函數 repeat()的實現,它被修改為用一個成員函數的指標作為參數:

 

typedef Screen& (Screen::*Action)();
 
Screen& Screen::repeat( Action op, int time s )
{
for ( int i = 0; i < times; ++i )
  (this->*op)();
 
return *this;
}

 

     參數 op 是一個成員函數的指標 它指向要被調用times 次的成員函數,若要為 repeat() 的參數提供預設實參,則聲明如下:

 
class Screen {
public:
Screen &repeat( Action = &Screen::forward, int = 1 );
// ...
};

    repeat()的調用如下 :

Screen myScreen;
myScreen.repeat(); // repeat( &Screen::forward, 1 );
myScreen.repeat( &Screen::down, 20 );

 

三、靜態類成員的指標

 

     在非靜態類成員的指標和靜態類成員的指引之間有一個區別,指向類成員的指標文法不能被用來引用類的靜態成員,靜態類成員是屬於該類的全域對象和函數,它們的指標是普通指標。(請記住靜態成員函數沒有 this指標)

 
     指向靜態類成員的指標的聲明看起來與非類成員的指標相同,解引用該指標不需要類對象,例如 我們來看一下類 Account: 

 

class Account {
public:
static void raiseInterest( double incr );
static double interest() { return _interestRate; }
double amount() { return _amount; }
private:
static double _interestRate;
double _amount;
string _owner;
};
 
inline void Account::raiseInterest( double incr )
{
_interestRate += incr;
}

    &_interestRate的類型是double* 而不是 :
// 不是 &_interestRate 的類型
double Account::*

     指向_interestRate的指標定義如下: 
// OK: 是 double*, 而不是 double Account::*
double *pd = &Account::_interestRate; 
    它被解引用的方式與普通指標一樣,不需要相關的類對象,例如:
Account unit;

// 用普通的解引用操作符
double daily = *pd /365 * unit._amount;

 

     但是,因為_interestRate和_amount 都是私人成員,所以我們需要使用靜態成員函數interest()和非靜態成員函數amount()。
     指向interest()的指標的類型是一個普通函數指標 
// 正確
double (*) ()

     而不是類 Account 的成員函數的指標 
// 不正確
double (Account::*) ()

     這個指標的定義和對 interest()的間接調用處理方式與非類的指標相同:

 
// ok: douple(*pf) () 不是 double(Account::*pf)()
double (*pf)() = &Account::interest;

double daily = pf () / 365 * unit.amount();

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.