我想问一下以下场景中编译器的行为: -
class A
{
public:
int n;
int *ptrClass;
};
int main()
{
A a;
int A::*ptr;
ptr = &A::n;
a.ptrClass = &a.n;
cout << "\nPointer points to the memory address: " << ptr;
cout << "\nPointer points to the value: " << a.*ptr;
cout << "\nPointer points to the memory address: " << a.ptrClass;
cout << "\nPointer points to the value: " << *a.ptrClass;
getchar();
return 0;
}
为什么ptr可以在没有任何引用的情况下在main中访问 A级, 就像a.ptr一样,我已经将ptr声明为int A :: * ptr; 是不是让ptr成为班级成员?
当我运行上面的程序时,我注意到,n的内存地址 班级 当由ptr和a.ptrClass提取时,A是不同的。没得到那个。任何 请解释。
问题可能非常基础,但请提供帮助。
答案 0 :(得分:4)
为什么在没有引用类A的情况下可以在main中访问ptr,类似于a.ptr,我已经将ptr声明为int A :: * ptr;它不能使ptr成为一个类成员吗?
int A::* ptr;
不会将ptr
声明为班级A
的成员。它宁愿将ptr
声明为指向类A
的类int
的数据成员的指针。例如:
class A {
public:
int i0;
int i1;
};
int main() {
int A::* mptr;
mptr = &A::i0;
mptr = &A::i1;
}
指向成员的指针并不指向特定的内存地址,它们非常类似于偏移量。例如:
int main() {
A a[2] = {{1, 2}, {3, 4}};
int A::* mptr = &A::i0;
// print the first data member (i0) of a[0] and a[1]
for(int i = 0; i < 2; ++i) {
std::cout << a[i].*mptr << "\n";
}
mptr = &A::i1;
// print the second data member (i1) of a[0] and a[1]
for(int i = 0; i < 2; ++i) {
std::cout << a[i].*mptr << "\n";
}
}
类A
的对象的可能内存布局:
class A {
public:
int i0;
int i1;
};
A a = {42, 21};
+-a--------------+ | +-i0-+ +-i1-+ | | | 42 | | 21 | | | +----+ +----+ | +----------------+
现在,我们添加指针:
int* pi1 = &a.i1;
int A::* mpi1 = &A::i1;
A* pa = &a;
+-a--------------+ | +-i0-+ +-i1-+ | | | 42 | | 21 | | | +----+ +----+ | +---------^------+ ^ | pi1 | pa
pi1
和pa
都指向特定对象。另一方面,mpi1
是 (= any)A
类型对象的偏移量:
+-A--------------+ | +-i0-+ +-i1-+ | | | | | | | | +----+ +----+ | ~~~~~~~~~~^ mpi1 | +----------------+
我们现在可以将mpi1
合并到pa
以获取特定对象a.i1
:
+-a--------------+ | +-i0-+ +-i1-+ | | | 42 | | 21 | | | +----+ +----+ | +---------^------+ ^~~~~~~~~~+ mpi1 | pa
这写成:
pa->*mpi1 // this expression refers to `a.i1` aka `*pi1`
所以我们可以确定:
&(pa->*mpi1) == pi1
如果我们将地址和偏移量解释为整数值,我们可能会得到类似的结果:
pa == 0x7fff39519328
pi1 == 0x7fff3951932c
mpi1 == 4
因此,我们可以看到8 + 4 == 12 == 0xc
,即&#34; pa + mpi1 == pi1&#34;。不要太过于字面意思,C ++中不允许执行此添加:pa + mpi1
不是合法的C ++。此外,我们无法保证所有平台都可以添加这两个值。但它解释了x86上的流行实现。
当我运行上面的程序时,我注意到,当由ptr和a.ptrClass提取时,类A的n的内存地址是不同的。没有那个。请解释。
cout << "\nPointer points to the memory address: " << ptr;
这一行实际上是
cout << "\nPointer points to the memory address: " << (bool)ptr;
与
相同cout << "\nPointer points to the memory address: " << (ptr != nullptr);
要看到这一点,请尝试
cout << boolalpha;
将布尔值打印为字符串 true / false 。
另一方面,这一行:
cout << "\nPointer points to the memory address: " << a.ptrClass;
转换为void*
。这是C ++中的 原始内存指针类型。指向特定对象的每个指针都可以转换为void*
,指向相同的内存位置。 cout << some_void_star
操作打印此void*
指向的原始内存地址。对于许多硬件体系结构,地址是或可以表示为整数。通常为操作cout << some_void_star
打印此整数表示。
指向成员的指针不是指向特定对象的指针,因此不允许将其转换为void*
(它们不指向特定地址)。将它们的值打印为整数是非常棘手的。你可以尝试:
#include <algorithm>
//...
// this code will probably produce wrong results on big endian architectures
static_assert(sizeof(unsigned long long) >= sizeof(mpi1));
unsigned long long as_integer;
std::copy_n(reinterpret_cast<char const*>(&mpi1), sizeof(mpi1),
reinterpret_cast<char*>(&as_integer));
std::cout << as_integer;