🥥类成员指针
2022-5-15
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property

 
成员指针(pointer to member)是指可以指向类的非静态成员的指针。一般情况下,指针可以指向一个对象,但是成员指针指的是类的成员,而非类的对象。
类的静态成员不属于任何对象,因此无须特殊的指向静态成员的指针,指向静态成员的指针与普通指针没有什么区别。
成员指针的类型囊括了类的类型以及成员的类型。当初始化一个这样的指针时,令其指向类的某个成员,但是不指定该成员所属的对象:直到使用成员指针时,才提供成员所属的对象。
 

数据成员指针

和其他指针一样,在声明成员指针时也可以使用*来表示当前声明的名字是一个指针。与普通指针不同的是,成员指针还必须包含成员所属的类。因此,必须在*之前添加classname:: ,以表示当前定义的指针可以指向classname的成员。例如:
pdata声明成"一个指向Screen类的const string"成员的指针。常量对象的数据成员本身也是常量,因此将指针声明成指向const string成员的指针意味着,pdata可以指向任何Screen对象的一个成员,而不管该Screen对象是否是常量。作为交换条件,只能使用pdata读取它所指的成员,而不能向它写入内容。
 
初始化一个成员指针时(或向它赋值)时,需指定它所指的成员,例如,可以令pdata指向某个非特定Screen对象的contents成员。
将取地址运算符作用于Screen类的成员而非内存中的一个该类对象。
 

使用数据成员指针

当初始化一个成员指针或为成员指针赋值时,该指针并没有指向任何数据成员指针指定了成员而非该成员所属的对象,只有当解引用成员指针时才提供对象的信息。
与成员访问运算符.->类似,也有两种成员指针访问运算符: .*->* ,这两个运算符可以解引用指针并获得该对象的成员:
 

函数返回数据成员指针

常规的访问控制规则对成员指针同样有效。例如,Screencontents成员是私有的,因此之前对于pdata的使用必须位于Screen类的成员或友元内部,否则程序将发生错误
因为数据成员一般情况下是私有的,所以通常不能直接获得数据成员的指针。如果一个像Screen这样的类希望可以访问它的contents成员,最好定义一个函数,令其返回值是指向该成员的指针:
data返回的是一个指向Screen类的const string成员的指针。函数体对contents成员使用了取地址运算符,因此函数将返回指向Screencontents成员的指针。
调用data函数时,将得到一个成员指针:
pdata指向Screen类的成员而非实际数据。如想要使用pdata,必须把它绑定到Screen类型的对象上:
 

成员函数指针

指定指向类的成员函数的指针。要想创建一个指向成员函数的指针,最简单的方法是使用auto来推断类型:
成员函数指针的定义要加上 classname::* 以及正常的函数指针的说明(返回类型与参数列表),如果成员函数是const成员或引用成员,必须在成员指针上体现出来,必须将const限定符或引用限定符包含进来。
和普通的函数指针类似,如果成员存在重载的问题,则我们必须显式地声明函数类型以明确指出我们想要使用的是哪个函数。例如,我们可以声明一个指针,令其指向含有两个形参的get
Screen::*两端的括号必不可少。如果没有这对括号的话,编译器将认为该声明是一个(无效的)函数声明:
 
和普通函数指针不同的是,在成员函数和指向该成员的指针之间不存在自动转换规则
 

使用成员函数指针

和使用指向数据成员的指针一样,使用.*或者->*运算符作用于指向成员函数的指针,以调用类的成员函数:
之所以(myScreen->*pmf)()(pScreen.*pmf2)(0, 0)的括号必不可少,原因是调用运算符的优先级要高于指针指向成员运算符的优先级。
 
 

使用成员指针的类型别名

下面的类型别名将Action定义为两参数get函数的同义词:
Action是某类型的另外一个名字,该类型是"指向Screen类的常量成员函数的指针,其中这个成员函数接受两个pos形参,返回一个char"。通过使用Action,可以简化指向get的指针定义。
和其他函数指针类似,可以将指向成员函数的指针作为某个函数的返回类型或形参类型。其中,指向成员的指针形参也可以拥有默认实参。
action是包含两个形参的函数,其中一个形参是Screen对象的引用,另一个形参是指向Screen成员函数的指针,成员函数必须接受两个pos形参并返回一个char。当调用action时,只需将Screen的一个符合要求的函数的指针或地址传入即可:
 

成员指针函数表

对于普通函数指针和指向成员函数的指针来说,一种常见的用法是将其存入一个函数表当中。如果一个类含有几个相同类型的成员,则这样一张表可以帮助我们从这些成员中选择一个。假定Screen类含有几个成员函数,每个成员函数负责将光标向指定的方向移动:
这几个新函数有一个共同点:它们都不接受任何参数,并且返回值是发生光标移动的Screen的引用。
希望定义一个move函数,使其可以调用上面任意一个函数并执行其对应的操作。为了支持这个新的函数,在Screen中添加一个静态成员,该成员是指向光标移动函数的指针的数组:
数组Menu依次保存每个光标移动函数的指针,这些函数将按照Directions中枚举成员对应的偏移量存储。move函数接受一个枚举成员并调用相应的函数:
move中的函数调用的原理是:首先获取索引值为cmMenu元素,该元素是指向Screen成员函数的指针。我们根据this所指的对象调用该元素所指的成员函数。
调用move函数时,给它传入一个表示光标移动方向的枚举成员:
剩下的工作就是定义并初始化函数表本身了:
 
 

将成员函数用作可调用对象

要想通过一个指向成员函数的指针进行函数调用,必须首先利用.*运算符或->*运算符将该指针绑定到特定的对象上。因此与普通的函数指针不同,成员指针不是一个可调用对象,这样的指针不支持函数调用运算符。
因为成员指针不是可调用对象,所以不能直接将一个指向成员函数的指针传递给算法。
如果我们想在一个stringvector中找到第一个空string,显然不能使用下面的语句:
find_if算法需要一个可调用对象,但提供给它的是一个指向成员函数的指针fp。因此在find_if的内部将执行如下形式的代码,从而导致无法通过编译:
显然该语句试图调用的是传入的对象,而非函数。
 

使用function生成一个可调用对象

从指向成员函数的指针获取可调用对象的一种方法是使用标准库模板function
我们告诉function一个事实,即empty是一个接受string参数并返回bool值的函数。通常情况下,执行成员函数的对象将被传给隐式的this形参。当我们想要使用function为成员函数生成一个可调用对象时,必须首先“翻译”该代码,使得隐式的形参变成显式的。
当一个function对象包含有一个指向成员函数的指针时,function类知道它必须使用正确的指向成员的指针运算符来执行函数调用。也就是说,可以认为在finc_if当中含有类似于如下形式的代码:
其中,fucntion将使用正确的指向成员的指针运算符。从本质上来看,function类将函数调用转换成了如下形式:
定义一个function对象时,必须指定该对象所能表示的函数类型,即可调用对象的形式。如果可调用对象是一个成员函数,则第一个形参必须表示该成员是在哪个(一般是隐式的)对象上执行的。同时,提供给function的形式中还必须指明对象是否是以指针或引用的形式传入的。
以定义fcn为例,想在string对象的序列上调用find_if,因此要求function生成一个接受string对象的可调用对象。又因为vector保存的是string的指针,所以必须指定function接受指针:
 

使用mem_fn产生可调用对象

mem_fn 可以在不提供函数签名的情况下生成一个可调用对象,这个函数也定义在functional头文件中:
由 mem_fn 生成的可调用对象可以在指针或对象上调用,而不必显式指出来:
可以认为 mem_fn 生成了一个重载了的调用操作符的可调用对象,其中一个以指针为参数,另一个以对象引用为参数。
 

使用bind生成可调用对象

bind也可以生成一个可调用对象:
与 mem_fn 一样,不需要指定所在的对象是指针还是引用,但需要显式使用占位符告知所在对象在第一个参数的位置。
 
 
  • C++
  • union:一种节省空间的类IO类
    目录