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