c++ 类及其布局和转换

标签 c++ oop polymorphism

我得到这段代码来测试我对 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]:
动态调度本身是一个实现定义的机制,但大多数(阅读所有已知)实现使用虚拟表和指针机制来实现它。
对于多态类(不从任何其他类派生)通常,虚拟指针存储为类的第一个元素。因此,可以合理地假设这是在您的环境中运行代码示例时最后一个案例中在幕后发生的事情。

Online sample :

#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/

相关文章:

c++ - COM 对象 DLL 加载问题

python - 理解 Django 的模型类

javascript - 函数中的“this”

java - JVM如何解析java中的重写和覆盖方法

c# - 为什么将派生类的实例分配给类型不是实例之一的变量,而是其基类

c++ - 关于 MFC C++ 多态性

c++ - 了解 Linux 中的动态库加载

C++ - GMP pow() 函数

c++ - 在 C++ 中使用 fscanf 读取带有 int 和 float 的选项卡式文件

java - 有什么办法可以减少这些线路吗?