我遇到了这个奇怪的代码片段,它编译得很好:

class Car
{
    public:
    int speed;
};

int main()
{
    int Car::*pSpeed = &Car::speed;
    return 0;
}

为什么c++有这个指针指向类的非静态数据成员?在实际代码中,这个奇怪的指针有什么用呢?


当前回答

另一个应用是侵入式列表。元素类型可以告诉列表它的next/prev指针是什么。所以列表不使用硬编码的名称,但仍然可以使用现有的指针:

// say this is some existing structure. And we want to use
// a list. We can tell it that the next pointer
// is apple::next.
struct apple {
    int data;
    apple * next;
};

// simple example of a minimal intrusive list. Could specify the
// member pointer as template argument too, if we wanted:
// template<typename E, E *E::*next_ptr>
template<typename E>
struct List {
    List(E *E::*next_ptr):head(0), next_ptr(next_ptr) { }

    void add(E &e) {
        // access its next pointer by the member pointer
        e.*next_ptr = head;
        head = &e;
    }

    E * head;
    E *E::*next_ptr;
};

int main() {
    List<apple> lst(&apple::next);

    apple a;
    lst.add(a);
}

其他回答

一个指向成员的指针的真实例子可以是std::shared_ptr的更窄的混叠构造函数:

template <typename T>
template <typename U>
shared_ptr<T>::shared_ptr(const shared_ptr<U>, T U::*member);

构造函数有什么用

假设你有一个结构体foo:

struct foo {
    int ival;
    float fval;
};

如果你给了一个foo对象一个shared_ptr对象,你可以使用构造函数将shared_ptr对象检索到它的成员ival或fval:

auto foo_shared = std::make_shared<foo>();
auto ival_shared = std::shared_ptr<int>(foo_shared, &foo::ival);

如果想将指针foo_shared->ival传递给某个需要shared_ptr的函数,这将非常有用

https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr

它是一个“指向成员的指针”——下面的代码说明了它的用法:

#include <iostream>
using namespace std;

class Car
{
    public:
    int speed;
};

int main()
{
    int Car::*pSpeed = &Car::speed;

    Car c1;
    c1.speed = 1;       // direct access
    cout << "speed is " << c1.speed << endl;
    c1.*pSpeed = 2;     // access via pointer to member
    cout << "speed is " << c1.speed << endl;
    return 0;
}

至于你为什么要这样做,它给了你另一种间接的层次,可以解决一些棘手的问题。但说实话,我从未在自己的代码中使用过它们。

编辑:我想不出一个令人信服的使用指针成员数据。指向成员函数的指针可以在可插拔的体系结构中使用,但是在这么小的空间里生成一个例子再次让我感到挫败。以下是我最好的(未经测试)尝试-一个Apply函数,在应用用户选择的成员函数到对象之前,会做一些前后处理:

void Apply( SomeClass * c, void (SomeClass::*func)() ) {
    // do hefty pre-call processing
    (c->*func)();  // call user specified function
    // do hefty post-call processing
}

c->*func周围的括号是必要的,因为->*操作符的优先级低于函数调用操作符。

我使用它的一种方式是,如果我有两个如何在一个类中做某事的实现,我想在运行时选择一个,而不必连续地通过if语句,即。

class Algorithm
{
public:
    Algorithm() : m_impFn( &Algorithm::implementationA ) {}
    void frequentlyCalled()
    {
        // Avoid if ( using A ) else if ( using B ) type of thing
        (this->*m_impFn)();
    }
private:
    void implementationA() { /*...*/ }
    void implementationB() { /*...*/ }

    typedef void ( Algorithm::*IMP_FN ) ();
    IMP_FN m_impFn;
};

显然,这只有在你觉得代码被敲打到足够的if语句减慢事情完成时才有用。在某个密集算法的深处。我仍然认为它比if语句更优雅,即使在它没有实际用途的情况下,但这只是我的观点。

IBM有更多关于如何使用它的文档。简单地说,您使用指针作为类的偏移量。你不能在它们所指向的类之外使用这些指针,所以:

  int Car::*pSpeed = &Car::speed;
  Car mycar;
  mycar.*pSpeed = 65;

It seems a little obscure, but one possible application is if you're trying to write code for deserializing generic data into many different object types, and your code needs to handle object types that it knows absolutely nothing about (for example, your code is in a library, and the objects into which you deserialize were created by a user of your library). The member pointers give you a generic, semi-legible way of referring to the individual data member offsets, without having to resort to typeless void * tricks the way you might for C structs.

这是我能想到的最简单的例子,它传达了这个特性很少相关的情况:

#include <iostream>

class bowl {
public:
    int apples;
    int oranges;
};

int count_fruit(bowl * begin, bowl * end, int bowl::*fruit)
{
    int count = 0;
    for (bowl * iterator = begin; iterator != end; ++ iterator)
        count += iterator->*fruit;
    return count;
}

int main()
{
    bowl bowls[2] = {
        { 1, 2 },
        { 3, 5 }
    };
    std::cout << "I have " << count_fruit(bowls, bowls + 2, & bowl::apples) << " apples\n";
    std::cout << "I have " << count_fruit(bowls, bowls + 2, & bowl::oranges) << " oranges\n";
    return 0;
}

这里需要注意的是传递给count_fruit的指针。这样就不必单独编写count_apples和count_oranges函数。