输出基类和继承类的内存内容

Print out memory content for base and inherited class

本文关键字:内存 基类 继承 输出      更新时间:2023-10-16

我试图添加一个print()成员函数,该函数将输出对象的内存内容,如下所示:

#include <iostream>
#include <string>
class A {
 public:
    virtual std::string print() {
        std::string s;
        s.append(reinterpret_cast<char*>(this), 0, sizeof(A));
        return s;
    }
};
class B : public A {
 public:
    B() : a('a') {}
    char a;
};
int main() {
    A a;
    B b;
    std::cout << "A "" << a.print() << ""n";
    std::cout << "B "" << b.print() << ""n";
    return 0;
}

如何打印B的整个长度,上面的清单只打印B类的A部分。

要安全地做到这一点,您必须覆盖B的虚函数:

std::string print() override {
    std::string s;
    s.append(reinterpret_cast<char*>(this), 0, sizeof(B));
    return s;
}

为什么?因为您不能确定A和B具有相同的地址(例如,如果有多个基类)。

如果您喜欢这样的转储函数,为了减少代码,您可以使用模板并在每次重写中调用模板。

使用模板化的非虚函数基。

template<class T> class PrintMe
{
    public:
         std::string print() const
         {
               std::string s;
               s.append(reinterpret_cast<char*>(this), 0, sizeof(T));
               return s;
         };
};
class A: public PrintMe<A>
{
     // whatever
};
class B: public PrintMe<B>
{
};
//   and in code which uses it
std::cout << some_b.print();

本质上,规则是任何类,X,需要打印自身的能力继承自PrintMe<X>

就我个人而言,我根本不会使用继承或将其作为类的成员函数,而是使用

template<class T> std::string print(const T &x)
{
    std::string s;
    s.append(reinterpret_cast<char*>(&x), 0, sizeof(x));
    return s;
}
// and in some code which needs this
std::cout << print(some_b);
//  or, more explicitly
 std::cout << print<B>(some_b);

注意,这完全避免了虚函数分派,而是依赖于编译时所识别的对象类型。

如何打印B…的整个长度

你可以考虑在类层次结构中"链接"你的打印方法。

您的计划无法处理不可打印的值。我也没有看到"十六进制"转换。但是,假设您的转换为char*是您想要的……

class A {
public:
   virtual std::string print() {
       std::string s;
       s.append(reinterpret_cast<char*>(this), 0, sizeof(A));
       return s;
   }
};
class B : public A {
public:
   B() : a('a') {}
   char a;
   virtual std::string print() {
       std::string s = A::print(); // get A contents
       s.append(reinterpret_cast<char*>(this), 0, sizeof(B));
       return s;
   }
};

不测试。

同样,这些问题依赖于实现。

根据我的经验(几乎完全是用g++), B的实例包含A和B的所有数据(我认为A在前面,即较低的地址)。A的实例只有A的数据,并且不能告诉,也不知道任何关于任何派生类的信息。

如果在你的实现中,B拥有A和B的所有数据(很容易分辨,只需打印sizeof(A)和sizeof(B)并与期望进行比较),你不需要在B::print()中调用A::print()。

还要注意——如果任何一个类都使用容器,那么您希望打印的容器数据可能不在类实例的堆栈空间中。容器(vector、堆、列表等)使用堆。所以你可以在堆栈上找到指针,而在其他地方找到数据。

update——

这里是揭示部分g++实现细节的一个小尝试。

我的解释是,它表明B类实例在B类数据属性的前面(在较低的地址)包含a类实例的数据属性。B的大小是a的两倍

对于这段代码,我删除了虚拟关键字。Virtual以我期望的方式影响这些对象的大小。但是没有关键字,大小正是我对uint64_t的期望。8字节(A)和16字节(B)。
  class A
  {
  public:
     A() :
        aData(0x3132333435363738)
        {
        }
     ~A(){ }
     std::string dump()
        {
           std::stringstream ss;
           ss << "     this: " << &(*this);
           ss << "    aData: " << &aData << std::endl;
           return(ss.str());
        }
     std::string show()
        {
           std::stringstream ss;
           ss << std::hex << "0X" << aData << std::endl;
           return ss.str();
        }
     uint64_t aData; // 8 bytes
  };

  class B : public A
  {
  public:
     B() : bData(0x3837363534333231)
        {
        }
     ~B(){ }
     uint64_t bData;  // 8 bytes
     std::string dump()
        {
           std::stringstream ss;
           ss << "     this: " << &(*this);
           ss << " A::aData: " << &(A::aData) << "  bData:" << &bData 
              <<  std::endl; 
           return(ss.str());
        }
     std::string show()
        {
           std::stringstream ss;
           ss << std::hex << "0x" << A::aData << "  0x" << bData 
           << std::endl;
           return ss.str();
        }
  };
  int t405(void)
  {
     A a;
     B b;
     std::cout << "nsizeof(a): " << sizeof(a) << std::endl;
     std::cout <<   "sizeof(b): " << sizeof(b) << std::endl;
     std::cout << "ninstance a: " << &a << std::endl;
     std::cout <<   "instance b: " << &b << std::endl;
     std::cout << "ninstance a - aData: "  << a.dump() << std::flush;
     std::cout << "ninstance b - bData: "  << b.dump() << std::flush;
     std::cout << "ninstance a show(): " << a.show() << std::flush;
     std::cout << "ninstance b show(): " << b.show() << std::flush;
     return(0);
  }

输出应该像这样:

sizeof(a): 8 sizeof(b): 16
instance a: 0x7ffe73f5b5d0 instance b: 0x7ffe73f5b5e0
instance a - aData:      this: 0x7ffe73f5b5d0    aData: 0x7ffe73f5b5d0
instance b - bData:      this: 0x7ffe73f5b5e0 A::aData: 0x7ffe73f5b5e0  bData:0x7ffe73f5b5e8
instance a show(): 0X3132333435363738
instance b show(): 0x3132333435363738  0x3837363534333231

首先可以#include <stdint.h>

现在你可以将你的对象转换为uint8_t,并通过for循环迭代它,(size = sizeof(object))。

通过printf:

将十六进制值打印到stdin
printf("%hu", val[i]);