首页 > 代码库 > C++ Primer 学习笔记_103_特殊工具与技术 --类成员指针

C++ Primer 学习笔记_103_特殊工具与技术 --类成员指针

特殊工具与技术

--类成员指针

 

成员指针可以做到:获得特定成员的指针,然后从一个对象或别的对象获得该成员.成员指针应该包含类的类型以及成员的类型.

 

.声明成员指针

测试类:

class Screen
{
public:
    typedef std::string::size_type index;
    char get() const;
    char get(index ht,index wd) const;

private:
    std::string contents;
    index cursor;
    index height,width;
};

 

 

1.定义数据成员指针

contents 的完全类型是Screen 类的成员, 其类型是 std::string。 因此, 可以指向 contents 的指针的完全类型是指向 std::string 类型的 Screen 类成员的指针,可以将指向 Screen 类的 string 成员的指针定义为 

std::string Screen::*ps_Screen;

可以用contents的地址初始化ps_Screen

std::string Screen::*ps_Screen = &Screen::contents;

还可以将height,widthcursor成员的指针定义为:

Screen::index Screen::*pindex;

 

    这是说,pindex 是具有 Screen::index 类型的 Screen 类成员的指针。可以将 width 的地址赋给这个指针

pindex = &Screen::height;
pindex = &Screen::width;


2.定义成员函数的指针

通过指定函数返回类型、形参表和类来定义成员函数的指针。例如,可引用不接受形参的 get 版本的 Screen 成员函数的指针具有如下类型:

char (Screen::*)() const;

这个get函数版本的指针可以定义和初始化如下:

char (Screen::*pmf)() const = &Screen::get;

也可以将带两个形参的 get 函数版本的指针定义为

char (Screen::*pmfTwoRef)(Screen::index,Screen::index) const;
pmfTwoRef = &Screen::get;

[小心!]

调用操作符的优先级高于成员指针操作符,因此,包围Screen::* 的括号是必要的,没有这个括号,编译器就将下面代码当作(无效的)函数声明: 

     // error: non-member function p cannot have const qualifier 
     char Screen::*p() const;


3.为成员指针使用类型别名

类型别名可以使成员指针更容易阅读。

    typedef char (Screen::*Action)(Screen::index,Screen::index) const;

Action 是类型“Screen 类的接受两个 index 类型形参并返回 char 的成员函数的指针的名字。使用类型别名,可以将 get 指针的定义简化为

Action get = &Screen::get;

也可以使用成员指针函数类型来声明函数形参和函数的返回类型:

Screen &action(Screen &,Action = &Screen::get);

可以通过传递Screen类中适当成员函数的指针或地址调用action函数:

Screen myScreen;
action(myScreen);
action(myScreen,get);
action(myScreen,&Screen::get);

 

.使用类成员的指针

类似于成员访问操作符 和 ->.* 和 -> 是两个新的操作符,它们使我们能够将成员指针绑定到实际对象。 这两个操作符的左操作数必须是类类型的对象或类类型的指针,右操作数是该类型的成员指针。

?  成员指针解引用操作符.*从对象或引用获取成员。 

?  成员指针箭头操作符->*通过对象的指针获取成员。

 

1.使用成员函数的指针

    char (Screen::*pmf)() const = &Screen::get;

    Screen myScreen;

    char ch1 = myScreen.get();
    char ch2 = (myScreen.*pmf)();   //equivalent call to get

    Screen *pMyScreen = &myScreen;

    ch1 = pMyScreen -> get();
    ch2 = (pMyScreen ->* pmf)();    //equivalent call to get

    //使用带有参数的版本
    char (Screen::*pmfTwoRef)(Screen::index,Screen::index) const = &Screen::get;
    ch1 = myScreen.get(0,0);
    ch2 = (myScreen.*pmfTwoRef)(0,0);   //equivalent call to get

 

[小心!]

因为调用操作符(())比成员指针操作符优先级高,所以调用(myScreen.*pmf)() 和 (pScreen->*pmf)() 需要括号

 

2.使用数据成员的指针

    Screen::index Screen::*pIndex = &Screen::width;

    Screen myScreen;
    Screen::index index1 = myScreen.width;
    Screen::index index2 = myScreen.*pIndex;

    Screen *pMyScreen = &myScreen;
    index1 = pMyScreen -> width;
    index2 = pMyScreen ->* pIndex;

 

3.成员指针函数表

函数指针和成员函数指针的一个公共用途:将它们存储在函数表中.函数表是函数指针的集合,在运行时从中选择给定调用.

对具有几个相同类型成员的类而言,可以使用这样的表来从这些成员的集合中选择一个。如:

class Screen
{
public:
    Screen &home();
    Screen &forward();
    Screen &back();
    Screen &up();
    Screen &down();

private:
    //...
};

这些新函数都不接受形参,并返回调用它的的对象的引用.

 

4.使用函数指针表

我们可能希望定义一个 move 函数,它可以调用这些函数中的任意一个并执行指定动作。 为了支持这个新函数, 我们将在 Screen 中增加一个 static 成员,该成员是光标移动函数的指针的数组:

class Screen
{
public:
//As Before

    typedef
    Screen &(Screen::*Action)();
    static Action Menu[];	//函数表

public:
    enum Directions {HOME,FORWARD,BACK,UP,DOWN};	//决定往哪方向移动
    Screen &move(Directions);

private:
    //As Before
};

 

名为Menu的数组将保存指向每个光标移动函数的指针,将在对应于Directions中枚举成员的偏移位置保存那些函数,move函数接受枚举成员并调用适当函数:

Screen& Screen::move(Directions cm)
{
    (this ->* Menu[cm])();

    return *this;
}

这样计算move内部的调用:获取由cm索引的Menu元素(该元素师Screen类成员函数的指针),代表 this 指向的对象调用该元素指向的成员函数。

Screen myScreen;
myScreen.move(Screen::HOME);
myScreen.move(Screen::UP);

5.定义成员函数指针表

//定义和初始化表本身
Screen::Action Screen::Menu[] =
{
    &Screen::home,
    &Screen::forward,
    &Screen::back,
    &Screen::up,
    &Screen::down
};

//P657 习题18.26~27
    Screen::index Screen::*pCursor = &Screen::cursor;

    typedef
    char (Screen::*pGetNoRef)() const;
    typedef
    char (Screen::*pGetTwoRef)() const;

    typedef
    Screen &(Screen::*pMoveNoRef)();
    typedef
    Screen &(Screen::*pMoveTwoRef)();

//习题18.28~31 类定义如下
class Screen
{
public:
    typedef std::string::size_type index;
    typedef
    Screen& (Screen::*pFunNoRef)();
    typedef
    Screen &(Screen::*Action)();

    char get() const;
    char get(index ht,index wd) const;

    Screen &home();
    Screen &forward();
    Screen &back();
    Screen &up();
    Screen &down();

private:
    static Action Menu[];

public:
    Screen(pFunNoRef rf = &Screen::home):pmf(rf) {}

    void setPmf(pFunNoRef rf = &Screen::home)
    {
        pmf = rf;
    }

    enum Directions {HOME,FORWARD,BACK,UP,DOWN};
    Screen &move(Directions);

private:
    std::string contents;
    index cursor;
    index height,width;

public:
    pFunNoRef pmf;
};