1

我得到了这段代码来测试我对 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个案例我都懂了,最后一个没懂。last 和 first 之间的区别在于虚拟析构函数。这有关系吗?如果是,如何?

4

2 回答 2

3

该代码示例具有实现定义的行为。无法保证任何情况下的输出。不能保证类的成员总是放置在连续的内存位置。它们之间可以添加填充字节。并且是否添加填充作为实现细节被忽略了。您对扮演角色的怀疑virtual可能是真的[注1:]。但需要注意的重要一点是即使没有virtual输出也不能保证。

参考:
C++11:9.2 类成员 [class.mem]

14) 分配具有相同访问控制(第 11 条)的(非联合)类的非静态数据成员,以便后面的成员在类对象中具有更高的地址。未指定具有不同访问控制的非静态数据成员的分配顺序 (11)。实现对齐要求可能会导致两个相邻的成员不会被立即分配;管理虚拟功能 (10.3) 和虚拟基类 (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
于 2013-01-19T13:19:24.807 回答
1

是的,虚拟成员函数(在本例中为析构函数)保存在“vtable”中,该“vtable”通常存储为类数据结构的第一个元素。

但是,请注意,C++ 标准中没有严格的规则说明情况就是如此。

于 2013-01-19T13:23:49.643 回答