打印基础和继承类的内存内容

时间:2016-07-01 18:59:58

标签: c++ c++03

我正在尝试添加一个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部分。

4 个答案:

答案 0 :(得分:2)

要安全地执行此操作,您必须覆盖B的虚函数:

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

为什么?因为您无法确定A和B是否具有相同的地址(例如,如果有multiple base classes)。

如果您喜欢这种转储功能,为了减少代码,您可以使用模板并在每次覆盖中调用模板。

答案 1 :(得分:1)

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

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);

请注意,这样可以完全避免虚函数调度,而是依赖于编译时识别的对象的类型。

答案 2 :(得分:0)

  

如何打印B的整个长度...

您可能会考虑&#39;链接&#39;您在类层次结构中的打印方法。

您的计划无法处理不可打印的值。我没有看到&#39; hex&#39;转换要么。但是,假设你的角色扮演你想要的东西......

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)并与期望值进行比较。),你不需要调用A :: print()在B :: print()里面。

另请注意 - 如果任一类使用容器,则您希望打印的容器数据可能不在类实例的堆栈空间中。容器(向量,堆,列表等)使用堆。所以你会在堆栈上找到指针,在其他地方找到数据。

更新 -

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

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

对于此代码,我删除了虚拟关键字。虚拟以我期望的方式影响这些对象的大小。但是没有关键字,大小正是我对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

答案 3 :(得分:-1)

首先,你可以 #include <stdint.h>

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

通过printf将其十六进制值打印到stdin:

printf("%hu", val[i]);