我遇到了这个奇怪的代码片段,它编译得很好:
class Car
{
public:
int speed;
};
int main()
{
int Car::*pSpeed = &Car::speed;
return 0;
}
为什么c++有这个指针指向类的非静态数据成员?在实际代码中,这个奇怪的指针有什么用呢?
我遇到了这个奇怪的代码片段,它编译得很好:
class Car
{
public:
int speed;
};
int main()
{
int Car::*pSpeed = &Car::speed;
return 0;
}
为什么c++有这个指针指向类的非静态数据成员?在实际代码中,这个奇怪的指针有什么用呢?
当前回答
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++的类型安全等价于C的offsetof(),它在stddef.h中定义:两者都返回某个字段位于类或结构中的信息。虽然在c++中也可以将offset()用于某些足够简单的类,但在一般情况下,它会失败,尤其是虚拟基类。因此指针成员被添加到标准中。它们还提供了更简单的语法来引用实际字段:
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;
至于为什么要使用offsetof()(或指向成员的指针),在stackoverflow的其他地方有很好的答案。这里有一个例子:宏的C偏移是如何工作的?
我认为,只有当成员数据相当大(例如,另一个相当庞大的类的对象),并且您有一些外部例程,只对该类的对象引用起作用时,才会想要这样做。你不想复制成员对象,所以这让你可以传递它。
您可以使用指向(同构)成员数据的指针数组来启用双重命名成员(即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;
}
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.
假设你有一个结构。在那个结构里面 *某种名字 *两个相同类型但含义不同的变量
struct foo {
std::string a;
std::string b;
};
好的,现在假设你在一个容器里有一堆foo:
// 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;
}
此时,调用readValues()将返回一个“input-a”和“input-b”一致的容器;所有的键都将出现,带有a或b或两者都有的foo。