C++ 中指向成员的指针在内部是如何工作的?
How do pointers to members in C++ work internally?
我想知道指向成员的指针在 C++ 中是如何工作的。
我看了一些地方,发现它们存储了某种偏移量。并使用这样一个事实,即成员在内存中的出现顺序与它们在 class/structure definition.But...
中声明的顺序相同
#include <iostream>
#include<typeinfo>
using namespace std;
struct S
{
S(int n): mi(n) {}
int mi;
int k;
int f() {return mi+k;}
};
int main()
{
S s(7);
int S::*pmi = &S::mi;
int S::*lop = &S::k;
int (S::*sf)() = &S::f;
cout<<&S::mi<<endl<<&S::k<<endl<<&S::f<<endl<<pmi<<endl<<lop<<endl<<sf<<endl;
cout<<typeid(lop).name()<<endl;
}
我希望看到某种抵消。但是,第一行中带有 cout<< 的所有值都将 1 作为输出。
我不明白,如果都给1,关于偏移量的信息存储在哪里?
如果你能解释一下到底发生了什么,那将非常有帮助。
Iostreams 插入运算符没有指向成员的指针的重载。
但是 bool
有一个重载,并且指向成员的指针可以隐式转换为 bool
- 指向成员的空指针将转换为 false,否则为 true。 bool
如果为真则流式传输为 1,如果为假则流式传输为 0。因此,您观察到的输出是 none 指向成员的指针为空的事实。
I tried incrementing by ++.. but that too doesn't work..
递增不起作用,因为指向成员的指针不支持指针运算。
So , is there any way of actually seeing the offset values?
任何东西都可以看作是一个字节数组,所以你可以通过一个小辅助函数来惊叹成员指针的内容:
template<class T>
void print_raw(const T& obj) {
const unsigned char* cptr = reinterpret_cast<const unsigned char*>(&obj);
const unsigned char* end = cptr + sizeof(T);
while(cptr < end) {
printf("%02hhx ", *cptr++); // is there simple way with iostreams?
}
std::cout << '\n';
}
std::cout << "pmi: ";
print_raw(pmi);
std::cout << "lop: ";
print_raw(lop);
std::cout << "sf: ";
print_raw(sf);
我系统上的示例输出:
pmi: 00 00 00 00 00 00 00 00
lop: 04 00 00 00 00 00 00 00
sf: f0 08 40 00 00 00 00 00 00 00 00 00 00 00 00 00
这可能会让您了解编译器是如何实现它们的。请注意,如果实现包含任何填充字节/位,则这些字节/位可能具有任何值,因此任何非零值都可能毫无意义。
成员对象指针输出看起来很明显。它看起来像对象中的 little endian 64 位偏移量。 mi
位于偏移量 0,k
位于偏移量 4 字节。
iostreams 无法直接打印指向成员的指针。
但是,任何指针都可以转换为布尔值。
它恰好是使 std::cout::operator<<
工作的唯一可能的转换,所以它没有歧义。
您的代码等同于:
cout
<< static_cast<bool>(&S::mi) << endl
<< static_cast<bool>(&S::k) << endl
<< static_cast<bool>(&S::f) << endl
我想知道指向成员的指针在 C++ 中是如何工作的。
我看了一些地方,发现它们存储了某种偏移量。并使用这样一个事实,即成员在内存中的出现顺序与它们在 class/structure definition.But...
中声明的顺序相同#include <iostream>
#include<typeinfo>
using namespace std;
struct S
{
S(int n): mi(n) {}
int mi;
int k;
int f() {return mi+k;}
};
int main()
{
S s(7);
int S::*pmi = &S::mi;
int S::*lop = &S::k;
int (S::*sf)() = &S::f;
cout<<&S::mi<<endl<<&S::k<<endl<<&S::f<<endl<<pmi<<endl<<lop<<endl<<sf<<endl;
cout<<typeid(lop).name()<<endl;
}
我希望看到某种抵消。但是,第一行中带有 cout<< 的所有值都将 1 作为输出。
我不明白,如果都给1,关于偏移量的信息存储在哪里?
如果你能解释一下到底发生了什么,那将非常有帮助。
Iostreams 插入运算符没有指向成员的指针的重载。
但是 bool
有一个重载,并且指向成员的指针可以隐式转换为 bool
- 指向成员的空指针将转换为 false,否则为 true。 bool
如果为真则流式传输为 1,如果为假则流式传输为 0。因此,您观察到的输出是 none 指向成员的指针为空的事实。
I tried incrementing by ++.. but that too doesn't work..
递增不起作用,因为指向成员的指针不支持指针运算。
So , is there any way of actually seeing the offset values?
任何东西都可以看作是一个字节数组,所以你可以通过一个小辅助函数来惊叹成员指针的内容:
template<class T>
void print_raw(const T& obj) {
const unsigned char* cptr = reinterpret_cast<const unsigned char*>(&obj);
const unsigned char* end = cptr + sizeof(T);
while(cptr < end) {
printf("%02hhx ", *cptr++); // is there simple way with iostreams?
}
std::cout << '\n';
}
std::cout << "pmi: ";
print_raw(pmi);
std::cout << "lop: ";
print_raw(lop);
std::cout << "sf: ";
print_raw(sf);
我系统上的示例输出:
pmi: 00 00 00 00 00 00 00 00
lop: 04 00 00 00 00 00 00 00
sf: f0 08 40 00 00 00 00 00 00 00 00 00 00 00 00 00
这可能会让您了解编译器是如何实现它们的。请注意,如果实现包含任何填充字节/位,则这些字节/位可能具有任何值,因此任何非零值都可能毫无意义。
成员对象指针输出看起来很明显。它看起来像对象中的 little endian 64 位偏移量。 mi
位于偏移量 0,k
位于偏移量 4 字节。
iostreams 无法直接打印指向成员的指针。
但是,任何指针都可以转换为布尔值。
它恰好是使 std::cout::operator<<
工作的唯一可能的转换,所以它没有歧义。
您的代码等同于:
cout
<< static_cast<bool>(&S::mi) << endl
<< static_cast<bool>(&S::k) << endl
<< static_cast<bool>(&S::f) << endl