什么是C ++中的Pointer-to-Member运算符-> *和。*?


Answers:


77

我希望这个例子能为您清除一切

//we have a class
struct X
{
   void f() {}
   void g() {}
};

typedef void (X::*pointer)();
//ok, let's take a pointer and assign f to it.
pointer somePointer = &X::f;
//now I want to call somePointer. But for that, I need an object
X x;
//now I call the member function on x like this
(x.*somePointer)(); //will call x.f()
//now, suppose x is not an object but a pointer to object
X* px = new X;
//I want to call the memfun pointer on px. I use ->*
(px ->* somePointer)(); //will call px->f();

现在,您不能使用x.somePointer(),或者px->somePointer()因为在类X中没有这样的成员。为此,使用了特殊的成员函数指针调用语法...您自己尝试一些示例,您就会习惯了


2
添加所需的大括号以补偿运算符的优先级。
马丁·约克

2
@Armen:哦,我知道现在发生了什么...我以前从未见过或需要这样的东西。(+1)很酷,但是我想现在的问题是:pointer与普通函数指针有何不同,它需要不同的语法?(例如,它更大吗?)
user541686

@Mehrdad:关键是要调用普通函数,只需提供参数即可。但是对于非静态成员函数,您还需要一个要在其上调用该函数的对象。因此,新的语法。
阿曼·齐鲁扬

1
指向成员函数的指针可能比常规指向函数的指针大:专门用于处理继承iirc
没用

谁能解释其&X::f运作方式?看到这个问题。
user2141130

21

编辑:顺便说一下,它对于虚拟成员函数指针很奇怪。

对于成员变量:

struct Foo {
   int a;
   int b;
};


int main ()
{
    Foo foo;
    int (Foo :: * ptr);

    ptr = & Foo :: a;
    foo .*ptr = 123; // foo.a = 123;

    ptr = & Foo :: b;
    foo .*ptr = 234; // foo.b = 234;
}

成员功能几乎相同。

struct Foo {
   int a ();
   int b ();
};


int main ()
{
    Foo foo;
    int (Foo :: * ptr) ();

    ptr = & Foo :: a;
    (foo .*ptr) (); // foo.a ();

    ptr = & Foo :: b;
    (foo .*ptr) (); // foo.b ();
}

3
+1表示语法适用于所有成员,而不仅仅是成员函数。我发现,尽管有很多有趣的潜在应用程序,但很少使用指向成员变量的指针。
乔恩·普迪

15

简而言之:您使用->.如果知道要访问的成员。而你使用->*.*如果你知道要访问哪个成员。

简单的侵入式列表示例

template<typename ItemType>
struct List {
  List(ItemType *head, ItemType * ItemType::*nextMemPointer)
  :m_head(head), m_nextMemPointer(nextMemPointer) { }

  void addHead(ItemType *item) {
    (item ->* m_nextMemPointer) = m_head;
    m_head = item;
  }

private:
  ItemType *m_head;

  // this stores the member pointer denoting the 
  // "next" pointer of an item
  ItemType * ItemType::*m_nextMemPointer;
};

6
+1的第一句话,虽然从来没有在我的生活我知道我要访问的成员,哈哈。:)
user541686

7

在内部,所谓的C ++成员“指针”更像是偏移量。您既需要这样的成员“指针”,也需要一个对象,以引用该对象中的成员。但是成员“指针”与指针语法一起使用,因此也称为名称。

可以使用两种方法获取对象:具有对该对象的引用,或者具有指向该对象的指针。

对于引用,.*将其与成员指针结合使用,对于指针,->*将其与成员指针结合使用。

但是,通常,如果可以避免,请勿使用成员指针。

他们遵循相当违反直觉的规则,并且可以绕过protected访问而无需任何明确的强制转换,也就是说,无意间……

干杯,……


+1,无需代码即可很好地解释它。:)问题:为什么我们不能像普通函数那样仅使用函数的地址?成员函数的指针与其他函数的指针是否不同?(例如,更大吗?)
user541686

1
@Mehrdad:如果可以有一个指向非虚函数成员的成员函数的指针,那么它的确可能只是地址。但是,虚拟性不是成员函数指针类型的一部分。因此,对于表示基于vtable的实现信息(它确定与指针类型相关联的类的vtable中的偏移量),其表示需要包括一些有关当前值是否引用虚拟函数以及是否为虚拟函数的信息。
干杯和健康。-Alf 2014年

7

当拥有普通指针(指向对象或基本类型)时,可以使用*取消引用的方式:

int a;
int* b = a;
*b = 5;     // we use *b to dereference b, to access the thing it points to

从概念上讲,我们使用成员函数指针执行相同的操作:

class SomeClass
{
   public:  void func() {}
};

// typedefs make function pointers much easier.
// this is a pointer to a member function of SomeClass, which takes no parameters and returns void
typedef void (SomeClass::*memfunc)();

memfunc myPointer = &SomeClass::func;

SomeClass foo;

// to call func(), we could do:
foo.func();

// to call func() using our pointer, we need to dereference the pointer:
foo.*myPointer();
// this is conceptually just:    foo  .  *myPointer  ();


// likewise with a pointer to the object itself:
SomeClass* p = new SomeClass;

// normal call func()
p->func();

// calling func() by dereferencing our pointer:
p->*myPointer();
// this is conceptually just:    p  ->  *myPointer  ();

我希望这有助于解释这一概念。我们实际上是在取消引用指向成员函数的指针。比这要复杂一些-它不是内存中函数的绝对指针,而只是一个偏移量,应用于foop更高版本。但是从概念上讲,我们正在取消引用它,就像我们将取消引用普通对象指针一样。


5

您不能将指向成员的指针作为普通指针取消引用-因为成员函数需要this指针,并且您必须以某种方式传递它。因此,您需要使用这两个运算符,对象在一侧,指针在另一侧,例如(object.*ptr)()

不过,可以考虑使用functionand bindstd::boost::,取决于您编写的是C ++ 03还是0x)而不是那些。


我认为这可能是最好的解释。
Marcin

1

指针到成员访问运算符:.*->*

指针到成员访问运营商,.*->*,是用于解引用指针构件结合的对象指针的对象,分别。此说明适用于指向数据成员的指针指向成员函数的指针

例如,考虑类Foo

struct Foo {
   int i;
   void f();
};

如果您声明成员指针,指向iPtrint数据成员Foo

int Foo::* iPtr;

您可以初始化此成员指针iPtr,使其指向该Foo::i成员:

iPtr = &Foo::i;

要取消引用该指针,您需要将其与Foo对象结合使用。

现在考虑对象foo和指向对象的指针fooPtr

Foo foo;
Foo* fooPtr = &foo;

然后,您可以iPtr结合使用foo或取消引用fooPtr

foo.*iPtr = 0;
fooPtr->*iPtr = 0;

类似地,您可以使用.*->*函数成员的指针。但是请注意,由于函数调用运算符(即)的()优先级高于.*和,因此您需要将它们括在括号中->*

void (Foo::*memFuncPtr)() = &Foo::f;

(foo.*memFuncPtr)();
(fooPtr->*memFuncPtr)();

结论:您需要一个对象来解除对成员的指针的引用,而使用哪个对象.*->*解除对成员的指针的引用,取决于该所需对象是直接提供还是通过对象指针提供。

C ++ 17 —std::invoke()改用

从C ++ 17开始,两个运算符的使用都可以由std::invoke功能模板代替。std::invoke提供了一种取消引用成员指针的统一方法,无论您将它们与对象还是对象指针结合使用,也不管指向成员指针是对应于数据成员的指针还是指向成员函数的指针

// dereference a pointer to a data member
std::invoke(iPtr, foo) = 0;      // with an object
std::invoke(iPtr, fooPtr) = 0;   // with an object pointer

// dereference a pointer to a member function
std::invoke(memFuncPtr, foo);      // with an object
std::invoke(memFuncPtr, fooPtr);   // with an object pointer

此统一语法与普通函数调用语法相对应,并且可以使编写通用代码更容易。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.