题记:本系列学习笔记(C++ Primer学习笔记)主要目的是讨论一些容易被大家忽略或者容易形成错误认识的内容。只适合于有了一定的C++基础的读者(至少学完一本C++教程)。
作者: tyc611, 2007-04-07
本文主要讨论C++中类成员的指针,这部分内容少有使用,也比较难于理解。
如果文中有错误或遗漏之处,敬请指出,谢谢!
需要使用类成员指针的情况是比较少见的,所以一般没人使用这个语言特性。下面先介绍类成员指针的使用方法,再介绍它的可能应用场合。
有时,我们需要在类外的其它地方直接获得类成员,这时我们就需要使用类成员指针。类成员指针与对象的指针不同,它不仅包括类的类型,还包括成员的类型。成员指针只应用于类的非static成员,因为static类成员不是任何对象的组成部分,只需使用普通指针即可。
声明成员指针
借用书上的一个例子,有这么一个类:
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;
};
若要声明contents成员的指针,应有如下形式:
string Screen::*ps;
若要声明cursor、height或width成员的指针,应有如下形式:
Screen::index Screen::*pi;
从上面的使用形式可以看出,使用类成员的指针与普通指针的区别在于,需要在类成员指针前面的加上类限定符,以指明是哪个类的成员指针。同样,在初始化指针时,也只能用相应类的相应类型的成员对指针进行初始化,例如:
ps = &Screen::contents;
pi = &Screen::cursor;
上面介绍了对类数据成员的指针的使用方法,下面对函数成员的指针进行介绍。
函数成员的指针与普通函数指针相比,也多了类限定符。由于类成员函数还有可能是const,所以const也成为成员函数指针声明的一部分。也就是说,函数成员的指针必须在三个方面与它所指函数的类型相匹配:
(1)函数形参的类型和数目;
(2)返回类型及是否为const函数;
(3)所属类的类型。
例如,要定义Screen的get成员的指针,可以如下定义:
char (Screen::*pmf)() const = &Screen::get; // not 'Screen::get'!
char (Screen::*pmf2)(Screen::index, Screen::index) const;
pmf2 = &Screen::get;
这里需要注意的是:(1)这里不存在函数类型到函数指针类型的自动转换(即类成员函数前面必须加上&,然后再给相应指针赋值);(2)运算符的优先级关系(注意指针外层的括号所起的作用);(3)函数到指针的自动类型匹配(注意两个重载版本的get对指针的赋值)。
使用类成员指针
要使用类成员指针所指对象,首先应当从类对象取得成员指针,再解引用指针,所以有如下两种操作符供使用:.*和->*。这两个操作符的左操作数必须是类类型的对象或类类型的指针,右操作数是该类型的成员指针。例如:
Screen sc;
char c = (sc.*pmf)(); // 等价于调用sc.get();
Screen *pS = ≻
c = (pS->*pmf)();
下面给出成员指针使用的完整例子,以方便读者更好的理解:
#include <string>
class Screen { friend void func(); // 声明func为类Screen的友元,否则无法使用类成员 public: typedef std::string::size_type index; char get() const { return 'a'; } char get(index ht, index wd) const { return 'b'; } private: std::string contents; index cursor; index heigth, width; };
void func() { std::string Screen:: *ps = &Screen::contents; Screen::index Screen:: *pi = &Screen::cursor; char (Screen:: *pmf) () const = &Screen::get; // not 'Screen::get'! char (Screen:: *pmf2) (Screen::index, Screen::index) const; pmf2 = &Screen::get; Screen sc; Screen *pS = ≻ Screen::index idx = sc.*pi; idx = pS->*pi; char c = (sc.*pmf)(); // 等价于调用sc.get(); c = (pS->*pmf)(); }
int main() { void (*pf)() = func; // 注意普通函数的指针的初始化,与成员指针比较 pf(); return 0; } |
成员指针的应用举例
当一个类有多个性质相同且类型相同的函数成员时,可以使用函数表来进行函数调用,产生用同一函数使用不同参数来达到不同操作的效果,而实际上是调用了不同的函数来实现的。下面给出这样的一个例子,方便读者有一个感性的认识:
#include <iostream> using namespace std;
class Screen { public: Screen& home() { cout << "Home" << endl; return *this; } Screen& forward() { cout << "Forward" << endl; return *this; } Screen& back() { cout << "Back" << endl; return *this; } Screen& up() { cout << "Up" << endl; return *this; } Screen& down() { cout << "down" << endl; return *this; } // function table typedef Screen& (Screen:: *Action)(); static Action Menu[]; // specify which direction to move enum Directions {HOME, FORWARD, BACK, UP, DOWN}; Screen& move(Directions); };
Screen::Action Screen::Menu[] = {&Screen::home, &Screen::forward, &Screen::back, &Screen::up, &Screen::down};
Screen& Screen::move(Directions dirc) { (this->*Menu[dirc])(); return *this; }
int main() { Screen sc; sc.move(Screen::HOME); sc.move(Screen::UP); return 0; } |
参考文献:
[1] C++ Primer(Edition 4)
[2] Thinking in C++(Edition 2)
[3] International Standard:ISO/IEC 14882:1998