C++ 指向类数据成员“::*”的指针
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/670734/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Pointer to class data member "::*"
提问by Ashwin Nanjappa
I came across this strange code snippet which compiles fine:
我遇到了这个奇怪的代码片段,它编译得很好:
class Car
{
public:
int speed;
};
int main()
{
int Car::*pSpeed = &Car::speed;
return 0;
}
Whydoes C++ have this pointer to a non-static data member of a class? Whatis the use of this strange pointer in real code?
为什么C++ 有指向类的非静态数据成员的 this 指针?什么是使用真正的代码,这个奇怪的指针?
采纳答案by Ashwin Nanjappa
It's a "pointer to member" - the following code illustrates its use:
它是一个“指向成员的指针”——下面的代码说明了它的用法:
#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;
}
As to whyyou would want to do that, well it gives you another level of indirection that can solve some tricky problems. But to be honest, I've never had to use them in my own code.
至于您为什么要这样做,它为您提供了另一个间接级别,可以解决一些棘手的问题。但老实说,我从来没有在我自己的代码中使用过它们。
Edit:I can't think off-hand of a convincing use for pointers to member data. Pointer to member functions can be used in pluggable architectures, but once again producing an example in a small space defeats me. The following is my best (untested) try - an Apply function that would do some pre &post processing before applying a user-selected member function to an object:
编辑:我无法立即想到对成员数据的指针的令人信服的使用。指向成员函数的指针可以在可插拔架构中使用,但是再一次在小空间中生成一个示例让我失望。以下是我最好的(未经测试的)尝试 - 一个 Apply 函数,它会在将用户选择的成员函数应用于对象之前进行一些前后处理:
void Apply( SomeClass * c, void (SomeClass::*func)() ) {
// do hefty pre-call processing
(c->*func)(); // call user specified function
// do hefty post-call processing
}
The parentheses around c->*func
are necessary because the ->*
operator has lower precedence than the function call operator.
括号c->*func
是必需的,因为->*
运算符的优先级低于函数调用运算符。
回答by John McFarlane
This is the simplest example I can think of that conveys the rare cases where this feature is pertinent:
这是我能想到的最简单的例子,它传达了与此功能相关的罕见情况:
#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;
}
The thing to note here is the pointer passed in to count_fruit. This saves you having to write separate count_apples and count_oranges functions.
这里要注意的是传入 count_fruit 的指针。这使您不必编写单独的 count_apples 和 count_oranges 函数。
回答by Johannes Schaub - litb
Another application are intrusive lists. The element type can tell the list what its next/prev pointers are. So the list does not use hard-coded names but can still use existing pointers:
另一个应用程序是侵入式列表。元素类型可以告诉列表它的下一个/上一个指针是什么。所以列表不使用硬编码的名称,但仍然可以使用现有的指针:
// 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);
}
回答by Tom
Here's a real-world example I am working on right now, from signal processing / control systems:
这是我现在正在研究的一个真实示例,来自信号处理/控制系统:
Suppose you have some structure that represents the data you are collecting:
假设您有一些表示正在收集的数据的结构:
struct Sample {
time_t time;
double value1;
double value2;
double value3;
};
Now suppose that you stuff them into a vector:
现在假设你把它们塞进一个向量中:
std::vector<Sample> samples;
... fill the vector ...
Now suppose that you want to calculate some function (say the mean) of one of the variables over a range of samples, and you want to factor this mean calculation into a function. The pointer-to-member makes it easy:
现在假设您想计算某个变量在一系列样本上的某个函数(比如平均值),并且您想将此平均值计算分解为一个函数。指向成员的指针使它变得容易:
double Mean(std::vector<Sample>::const_iterator begin,
std::vector<Sample>::const_iterator end,
double Sample::* var)
{
float mean = 0;
int samples = 0;
for(; begin != end; begin++) {
const Sample& s = *begin;
mean += s.*var;
samples++;
}
mean /= samples;
return mean;
}
...
double mean = Mean(samples.begin(), samples.end(), &Sample::value2);
Note Edited 2016/08/05 for a more concise template-function approach
注意 已编辑 2016/08/05 以获得更简洁的模板函数方法
And, of course, you can template it to compute a mean for any forward-iterator and any value type that supports addition with itself and division by size_t:
而且,当然,您可以将其模板化以计算任何前向迭代器和任何支持与自身相加和除以 size_t 的值类型的均值:
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);
EDIT - The above code has performance implications
编辑 - 上面的代码有性能影响
You should note, as I soon discovered, that the code above has some serious performance implications. The summary is that if you're calculating a summary statistic on a time series, or calculating an FFT etc, then you should store the values for each variable contiguously in memory. Otherwise, iterating over the series will cause a cache miss for every value retrieved.
你应该注意到,正如我很快发现的那样,上面的代码有一些严重的性能影响。总结是,如果您正在计算时间序列的汇总统计数据,或计算 FFT 等,那么您应该将每个变量的值连续存储在内存中。否则,对系列进行迭代将导致检索到的每个值的缓存未命中。
Consider the performance of this code:
考虑这段代码的性能:
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;
On many architectures, one instance of Sample
will fill a cache line. So on each iteration of the loop, one sample will be pulled from memory into the cache. 4 bytes from the cache line will be used and the rest thrown away, and the next iteration will result in another cache miss, memory access and so on.
在许多体系结构上,一个实例Sample
将填充缓存行。因此,在循环的每次迭代中,都会将一个样本从内存中提取到缓存中。缓存行中的 4 个字节将被使用,其余的将被丢弃,下一次迭代将导致另一个缓存未命中、内存访问等。
Much better to do this:
这样做更好:
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;
Now when the first x value is loaded from memory, the next three will also be loaded into the cache (supposing suitable alignment), meaning you don't need any values loaded for the next three iterations.
现在,当第一个 x 值从内存加载时,接下来的三个值也将加载到缓存中(假设合适的对齐方式),这意味着您不需要为接下来的三个迭代加载任何值。
The above algorithm can be improved somewhat further through the use of SIMD instructions on eg SSE2 architectures. However, these work muchbetter if the values are all contiguous in memory and you can use a single instruction to load four samples together (more in later SSE versions).
通过在例如 SSE2 架构上使用 SIMD 指令,可以进一步改进上述算法。但是,如果这些值在内存中都是连续的,并且您可以使用单个指令将四个样本一起加载(在更高的 SSE 版本中更多),则这些工作会更好。
YMMV - design your data structures to suit your algorithm.
YMMV - 设计您的数据结构以适合您的算法。
回答by peterchen
You can later access this member, on anyinstance:
您可以稍后在任何实例上访问此成员:
int main()
{
int Car::*pSpeed = &Car::speed;
Car myCar;
Car yourCar;
int mySpeed = myCar.*pSpeed;
int yourSpeed = yourCar.*pSpeed;
assert(mySpeed > yourSpeed); // ;-)
return 0;
}
Note that you do need an instance to call it on, so it does not work like a delegate.
It is used rarely, I've needed it maybe once or twice in all my years.
请注意,您确实需要一个实例来调用它,因此它不像委托那样工作。
它很少使用,我多年来可能需要一两次。
Normally using an interface (i.e. a pure base class in C++) is the better design choice.
通常使用接口(即 C++ 中的纯基类)是更好的设计选择。
回答by AHelps
IBMhas some more documentation on how to use this. Briefly, you're using the pointer as an offset into the class. You can't use these pointers apart from the class they refer to, so:
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.
这似乎有点晦涩,但一种可能的应用是,如果您尝试编写用于将通用数据反序列化为许多不同对象类型的代码,并且您的代码需要处理它完全不了解的对象类型(例如,您的代码是在库中,并且您反序列化的对象是由库的用户创建的)。成员指针为您提供了一种通用的、半清晰的方式来引用单个数据成员偏移量,而不必像 C 结构那样使用无类型的 void * 技巧。
回答by Alex B
It makes it possible to bind member variables and functions in the uniform manner. The following is example with your Car class. More common usage would be binding std::pair::first
and ::second
when using in STL algorithms and Boost on a map.
它使得以统一的方式绑定成员变量和函数成为可能。以下是您的 Car 类的示例。更常见的用法是绑定std::pair::first
以及::second
在 STL 算法和地图上的 Boost 中使用时。
#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;
}
回答by Functastic
You can use an array of pointer to (homogeneous) member data to enable a dual, named-member (i.e. x.data) and array-subscript (i.e. x[idx]) interface.
您可以使用指向(同类)成员数据的指针数组来启用双重、命名成员(iexdata)和数组下标(即 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;
}
回答by Troubadour
One way I've used it is if I have two implementations of how to do something in a class and I want to choose one at run-time without having to continually go through an if statement i.e.
我使用它的一种方法是,如果我有两个如何在类中做某事的实现,并且我想在运行时选择一个而不必不断地通过 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;
};
Obviously this is only practically useful if you feel the code is being hammered enough that the if statement is slowing things done eg. deep in the guts of some intensive algorithm somewhere. I still think it's more elegant than the if statement even in situations where it has no practical use but that's just my opnion.
显然,这只有在您觉得代码被敲打得足够多以至于 if 语句减慢了完成的事情时才有用。深入某处某个密集算法的内部。我仍然认为它比 if 语句更优雅,即使在它没有实际用途的情况下也是如此,但这只是我的选择。
回答by Arijit Dey
Pointers to classes are not realpointers; a class is a logical construct and has no physical existence in memory, however, when you construct a pointer to a member of a class it gives an offset into an object of the member's class where the member can be found; This gives an important conclusion: Since static members are not associated with any object so a pointer to a member CANNOT point to a static member(data or functions) whatsoeverConsider the following:
指向类的指针不是真正的指针;一个类是一个逻辑构造,在内存中没有物理存在,但是,当你构造一个指向类成员的指针时,它会提供一个偏移量到可以找到该成员的成员类的对象中;这给出了一个重要的结论:由于静态成员不与任何对象相关联,因此指向成员的指针不能指向任何静态成员(数据或函数)考虑以下事项:
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;
}
Source: The Complete Reference C++ - Herbert Schildt 4th Edition
来源:C++ 完整参考 - Herbert Schildt 第 4 版