C++ 指向类数据成员的指针";::*&引用;

C++ 指向类数据成员的指针";::*&引用;,c++,class,pointers,c++-faq,C++,Class,Pointers,C++ Faq,我遇到了这个奇怪的代码片段,它编译得很好: class Car { public: int speed; }; int main() { int Car::*pSpeed = &Car::speed; return 0; } 强>为什么C++有一个指针指向类的非静态数据成员?这个奇怪的指针在实际代码中的用途是什么?您可以稍后在任何实例上访问此成员: int main() { int Car::*pSpeed = &Car::spee

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

class Car
{
    public:
    int speed;
};

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

<>强>为什么C++有一个指针指向类的非静态数据成员?这个奇怪的指针在实际代码中的用途是什么?

您可以稍后在任何实例上访问此成员:

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

  int mySpeed = myCar.*pSpeed;
  int yourSpeed = yourCar.*pSpeed;

  assert(mySpeed > yourSpeed); // ;-)

  return 0;
}
请注意,您确实需要一个实例来调用它,因此它不像委托那样工作。
它很少使用,在我的一生中,我可能需要它一两次

通常使用接口(即C++中的纯基类)是更好的设计选择。

有更多关于如何使用接口的文档。简单地说,您使用指针作为类的偏移量。除了它们引用的类之外,不能使用这些指针,因此:

  int Car::*pSpeed = &Car::speed;
  Car mycar;
  mycar.*pSpeed = 65;
这似乎有点模糊,但一个可能的应用是,如果您试图编写代码将泛型数据反序列化为许多不同的对象类型,并且您的代码需要处理它完全不知道的对象类型(例如,您的代码在库中,而您反序列化到其中的对象是由库的用户创建的)。成员指针为您提供了一种通用的、半易读的方式来引用单个数据成员偏移量,而不必像C结构那样使用无类型void*技巧。

这是一种“指向成员的指针”-以下代码说明了其用途:

#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;
}

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

我认为只有在成员数据非常大(例如,另一个相当大的类的对象)的情况下,才需要这样做,并且您有一些只对该类对象的引用起作用的外部例程。您不想复制成员对象,因此这可以让您传递它。

我使用它的一种方法是,如果我有两个实现,说明如何在一个类中执行某项操作,并且我希望在运行时选择一个,而不必不断地通过if st阿泰门特,即

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语句更优雅,即使在没有实际用途的情况下,但这只是我的选择。

它使它成为可能能够以统一的方式绑定成员变量和函数。以下是Car类的示例。更常见的用法是在STL算法中使用时绑定
std::pair::first
::second

#include <list>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>


class Car {
public:
    Car(int s): speed(s) {}
    void drive() {
        std::cout << "Driving at " << speed << " km/h" << std::endl;
    }
    int speed;
};

int main() {

    using namespace std;
    using namespace boost::lambda;

    list<Car> l;
    l.push_back(Car(10));
    l.push_back(Car(140));
    l.push_back(Car(130));
    l.push_back(Car(60));

    // Speeding cars
    list<Car> s;

    // Binding a value to a member variable.
    // Find all cars with speed over 60 km/h.
    remove_copy_if(l.begin(), l.end(),
                   back_inserter(s),
                   bind(&Car::speed, _1) <= 60);

    // Binding a value to a member function.
    // Call a function on each car.
    for_each(s.begin(), s.end(), bind(&Car::drive, _1));

    return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
班车{
公众:
汽车(内部)(s):速度(s){}
无效驱动器(){

std::cout另一个应用程序是入侵列表。元素类型可以告诉列表下一个/上一个指针是什么。因此列表不使用硬编码名称,但仍然可以使用现有指针:

// 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);
}
//假设这是某个现有结构。我们想使用
//一个列表。我们可以告诉它下一个指针
//下一个是苹果。
结构苹果{
int数据;
苹果*下一个;
};
//最小侵入列表的简单示例。可以指定
//成员指针也作为模板参数,如果需要:
//模板
模板
结构列表{
列表(E*E::*next_ptr):头(0),next_ptr(next_ptr){}
无效添加(E&E){
//通过成员指针访问其下一个指针
e、 *next_ptr=头部;
头=&e;
}
E*头部;
E*E::*下一步;
};
int main(){
列出lst(&apple::next);
苹果a;
第1条添加(a);
}

您可以使用指向(同构)成员数据的指针数组来启用双命名成员(即x.data)和数组下标(即x[idx])接口。

#include <cassert>
#include <cstddef>

struct vector3 {
    float x;
    float y;
    float z;

    float& operator[](std::size_t idx) {
        static float vector3::*component[3] = {
            &vector3::x, &vector3::y, &vector3::z
        };
        return this->*component[idx];
    }
};

int main()
{
    vector3 v = { 0.0f, 1.0f, 2.0f };

    assert(&v[0] == &v.x);
    assert(&v[1] == &v.y);
    assert(&v[2] == &v.z);

    for (std::size_t i = 0; i < 3; ++i) {
        v[i] += 1.0f;
    }

    assert(v.x == 1.0f);
    assert(v.y == 2.0f);
    assert(v.z == 3.0f);

    return 0;
}
#包括
#包括
结构向量3{
浮动x;
浮动y;
浮动z;
浮点和运算符[](标准::大小\u t idx){
静态浮点向量3::*组件[3]={
&向量3::x、&vector3::y、&vector3::z
};
返回此->*组件[idx];
}
};
int main()
{
向量3v={0.0f,1.0f,2.0f};
断言(&v[0]==&v.x);
断言(&v[1]==&v.y);
断言(&v[2]==&v.z);
对于(标准::尺寸\u t i=0;i<3;++i){
v[i]+=1.0f;
}
断言(v.x==1.0f);
断言(v.y==2.0f);
断言(v.z==3.0f);
返回0;
}

以下是我正在研究的一个真实示例,来自信号处理/控制系统:

假设您有一些表示正在收集的数据的结构:

struct Sample {
    time_t time;
    double value1;
    double value2;
    double value3;
};
现在假设将它们填充到向量中:

std::vector<Sample> samples;
... fill the vector ...
现在,当从内存加载第一个x值时,接下来的三个x值也将加载到缓存中(假设适当对齐),这意味着您不需要为接下来的三个迭代加载任何值

通过在eg SSE2体系结构上使用SIMD指令,可以进一步改进上述算法。但是,如果这些值在内存中都是连续的,并且您可以使用一条指令将四个样本一起加载(在以后的SSE版本中会有更多),则这些算法的效果会更好


YMMV-设计您的数据结构以适应您的算法。

这是我能想到的最简单的示例,它传达了与此功能相关的罕见情况:

#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;
}
#包括
班级杯{
公众:
苹果;
橘子;
};
整数计数水果(碗*开始,碗*结束,整数碗::*水果)
{
整数计数=0;
for(bowl*iterator=begin;iterator!=end;++iterator)
计数+=迭代器->*结果;
返回计数;
}
int main()
{
碗碗[2]={
{ 1, 2 },
{ 3, 5 }
};

std::cout下面是一个指向数据成员的指针可能有用的示例:

#include <iostream>
#include <list>
#include <string>

template <typename Container, typename T, typename DataPtr>
typename Container::value_type searchByDataMember (const Container& container, const T& t, DataPtr ptr) {
    for (const typename Container::value_type& x : container) {
        if (x->*ptr == t)
            return x;
    }
    return typename Container::value_type{};
}

struct Object {
    int ID, value;
    std::string name;
    Object (int i, int v, const std::string& n) : ID(i), value(v), name(n) {}
};

std::list<Object*> objects { new Object(5,6,"Sam"), new Object(11,7,"Mark"), new Object(9,12,"Rob"),
    new Object(2,11,"Tom"), new Object(15,16,"John") };

int main() {
    const Object* object = searchByDataMember (objects, 11, &Object::value);
    std::cout << object->name << '\n';  // Tom
}
#包括
#包括
#包括
模板
typename容器::值\类型searchByDataMember(常量容器和容器、常量T&T、DataPtr){
for(const typename Container::value_typ)
template<typename Titer, typename S>
S mean(Titer begin, const Titer& end, S std::iterator_traits<Titer>::value_type::* var) {
    using T = typename std::iterator_traits<Titer>::value_type;
    S sum = 0;
    size_t samples = 0;
    for( ; begin != end ; ++begin ) {
        const T& s = *begin;
        sum += s.*var;
        samples++;
    }
    return sum / samples;
}

struct Sample {
    double x;
}

std::vector<Sample> samples { {1.0}, {2.0}, {3.0} };
double m = mean(samples.begin(), samples.end(), &Sample::x);
struct Sample {
  float w, x, y, z;
};

std::vector<Sample> series = ...;

float sum = 0;
int samples = 0;
for(auto it = series.begin(); it != series.end(); it++) {
  sum += *it.x;
  samples++;
}
float mean = sum / samples;
struct Samples {
  std::vector<float> w, x, y, z;
};

Samples series = ...;

float sum = 0;
float samples = 0;
for(auto it = series.x.begin(); it != series.x.end(); it++) {
  sum += *it;
  samples++;
}
float mean = sum / samples;
#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;
}
#include <iostream>
#include <list>
#include <string>

template <typename Container, typename T, typename DataPtr>
typename Container::value_type searchByDataMember (const Container& container, const T& t, DataPtr ptr) {
    for (const typename Container::value_type& x : container) {
        if (x->*ptr == t)
            return x;
    }
    return typename Container::value_type{};
}

struct Object {
    int ID, value;
    std::string name;
    Object (int i, int v, const std::string& n) : ID(i), value(v), name(n) {}
};

std::list<Object*> objects { new Object(5,6,"Sam"), new Object(11,7,"Mark"), new Object(9,12,"Rob"),
    new Object(2,11,"Tom"), new Object(15,16,"John") };

int main() {
    const Object* object = searchByDataMember (objects, 11, &Object::value);
    std::cout << object->name << '\n';  // Tom
}
struct foo {
    std::string a;
    std::string b;
};
// key: some sort of name, value: a foo instance
std::map<std::string, foo> container;
void readDataFromText(std::istream & input, std::map<std::string, foo> & container, std::string foo::*storage) {
    std::string line, name, value;

    // while lines are successfully retrieved
    while (std::getline(input, line)) {
        std::stringstream linestr(line);
        if ( line.empty() ) {
            continue;
        }

        // retrieve name and value
        linestr >> name >> value;

        // store value into correct storage, whichever one is correct
        container[name].*storage = value;
    }
}

std::map<std::string, foo> readValues() {
    std::map<std::string, foo> foos;

    std::ifstream a("input-a");
    readDataFromText(a, foos, &foo::a);
    std::ifstream b("input-b");
    readDataFromText(b, foos, &foo::b);
    return foos;
}
class x {
public:
    int val;
    x(int i) { val = i;}

    int get_val() { return val; }
    int d_val(int i) {return i+i; }
};

int main() {
    int (x::* data) = &x::val;               //pointer to data member
    int (x::* func)(int) = &x::d_val;        //pointer to function member

    x ob1(1), ob2(2);

    cout <<ob1.*data;
    cout <<ob2.*data;

    cout <<(ob1.*func)(ob1.*data);
    cout <<(ob2.*func)(ob2.*data);


    return 0;
}
template <typename T>
template <typename U>
shared_ptr<T>::shared_ptr(const shared_ptr<U>, T U::*member);
struct foo {
    int ival;
    float fval;
};
auto foo_shared = std::make_shared<foo>();
auto ival_shared = std::shared_ptr<int>(foo_shared, &foo::ival);
struct C { int a; int b; } c;
int C::* intptr = &C::a;       // or &C::b, depending on the field wanted
c.*intptr += 1;
struct C { int a; int b; } c;
int intoffset = offsetof(struct C, a);
* (int *) (((char *) (void *) &c) + intoffset) += 1;