我得到这段代码来测试我对 cpp 的理解,我很困惑:
#include "stdafx.h"
#include <iostream>
#include <cstddef>
using namespace std;
class A
{
public:
A() : m_x(0) { }
public:
static ptrdiff_t member_offset(const A &a)
{
const char *p = reinterpret_cast<const char*>(&a);
const char *q = reinterpret_cast<const char*>(&a.m_x);
return q - p;
}
private:
int m_x;
};
class B
: public A
{
public:
B() : m_x('a') { }
public:
static int m_n;
public:
static ptrdiff_t member_offset(const B &b)
{
const char *p = reinterpret_cast<const char*>(&b);
const char *q = reinterpret_cast<const char*>(&b.m_x);
return q - p;
}
private:
char m_x;
};
int B::m_n = 1;
class C
{
public:
C() : m_x(0) { }
virtual ~C() { }
public:
static ptrdiff_t member_offset(const C &c)
{
const char *p = reinterpret_cast<const char*>(&c);
const char *q = reinterpret_cast<const char*>(&c.m_x);
return q - p;
}
private:
int m_x;
};
int _tmain(int argc, _TCHAR* argv[])
{
A a;
B b;
C c;
std::cout << ((A::member_offset(a) == 0) ? 0 : 1);
std::cout << ((B::member_offset(b) == 0) ? 0 : 2);
std::cout << ((A::member_offset(b) == 0) ? 0 : 3);
std::cout << ((C::member_offset(c) == 0) ? 0 : 4);
std::cout << std::endl;
return 0;
}
答案是 0204。 前3个案例我都看懂了,最后一个我还不懂。最后一个和第一个之间的区别是虚拟析构函数。这有关系吗?如果是,如何?
最佳答案
代码示例有一个 implementation defined behavior 强>。无法保证任何情况的输出。 不保证类的成员总是放置在连续的内存位置。它们之间可以添加填充字节。是否添加填充作为实现细节被忽略。你对virtual
的怀疑扮演一个角色可能是真的[注 1:]。但需要注意的重要一点是即使没有 virtual
无法保证输出。
引用:
C++11: 9.2 类成员 [class.mem]
14) Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (11). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).
[注1]:
动态调度本身是一个实现定义的机制,但大多数(阅读所有已知)实现使用虚拟表和指针机制来实现它。
对于多态类(不从任何其他类派生)通常,虚拟指针存储为类的第一个元素。因此,可以合理地假设这是在您的环境中运行代码示例时最后一个案例中在幕后发生的事情。
#include<iostream>
using std::cout;
using std::endl;
class B;
typedef void (*HANDLE_DOSOMETHING)(B *const, int q);
class B
{
public:
virtual void doSomething(int q)
{
std::cout<<"B::doSomething()"<<q<<endl;
}
void dummy()
{
HANDLE_DOSOMETHING *f1ptr = NULL;
int *vtbl = NULL;
int *vptr = (int *)this; // address of the object
vtbl = (int *)*vptr; //address of the VTABLE
f1ptr = (HANDLE_DOSOMETHING *)&(vtbl[0]); //address of the 1st virtual function
(*f1ptr)(this, 55);
}
};
int main()
{
B objb;
objb.dummy();
return 0;
}
输出:
B::doSomething()55
关于c++ 类及其布局和转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14414745/