Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/127.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++编程实现成员的内存布局_C++ - Fatal编程技术网

C++编程实现成员的内存布局

C++编程实现成员的内存布局,c++,C++,假设在一个程序中,我得到: class Foo { int x; double y; char z; }; class Bar { Foo f1; int t; Foo f2; }; int main() { Bar b; bar.f1.z = 'h'; bar.f2.z = 'w'; ... some crap setting value of b; FILE *f = fopen("dump", "wb"); // c-style file

假设在一个程序中,我得到:

class Foo {
  int x;
  double y;
  char z;
};

class Bar {
  Foo f1;
  int t;
  Foo f2;
};

int main() {
  Bar b;
  bar.f1.z = 'h';
  bar.f2.z = 'w';
  ... some crap setting value of b;
  FILE *f = fopen("dump", "wb"); // c-style file
  fwrite(&b, sizeof(Bar), 1, f);
}
假设在另一个程序中,我有:

int main() {
  File *f = fopen("dump", "rb");
  std::string Foo = "int x; double y; char z;";
  std::string Bar = "Foo f1; int t; Foo f2;";

  // now, given this is it possible to read out
  // the value of bar.f1.z and bar.f2.z set earlier?
}
我想问的是:
如果我有一个类的类型,我能知道C++是如何编排的吗?

你需要研究序列化。人们一直在推荐一个图书馆,Boost Serialization

FWIW,我建议不要在类、结构和联合上使用fwrite或std::ostream::write。允许编译器在成员之间插入填充,因此可能会写出垃圾。此外,指针的序列化也不是很好

为了回答您的问题,为了确定从哪个结构加载数据,您需要某种类型的哨兵来指示对象类型。这可以是从枚举到对象名称的任何内容


还要研究工厂设计模式。

您不能假设表示条的字节顺序。如果文件穿过系统,或者该程序使用不同的标志编译,那么您将以不同的顺序进行读取和写入

我已经找到了解决这个问题的方法,但它可能只适用于非常简单的类型

我引用了raknet的一篇教程:

#pragma pack(push, 1)
struct structName
{
  unsigned char typeId; // Your type here
  // Your data here
};
#pragma pack(pop)
注意到布拉格玛填料函,1和布拉格玛填料函了吗?这些强制编译器(在本例中为VC++)将结构打包为字节对齐。查看编译器文档以了解更多信息


你想要序列化。

我不太清楚你在问什么,所以我要跳过一步

如果确实需要确定字段在结构中的位置,请使用

请注意链接页面中的POD限制。这是一个C宏,包含在C++中,用于兼容性的原因。现在我们应该使用成员指针,尽管成员指针不能解决所有相同的问题

offsetof基本上是在地址0处想象一个结构实例,然后查看您感兴趣的字段的地址。如果您的结构/类使用多重继承或虚拟继承,则会出现可怕的错误,因为查找字段通常需要在虚拟表中进行检查。由于地址为零的虚拟实例不存在,因此它没有虚拟表指针,因此您可能会遇到某种访问冲突崩溃

一些编译器可以解决这一问题,因为它们已经用一个内在函数替换了传统的offsetof宏,该函数知道结构的布局,而不必尝试进行假想的实例欺骗。即便如此,最好不要依赖于此

不过,对于POD结构,offsetof是查找特定字段偏移量的一种方便方法,也是一种安全的方法,因为它决定了实际偏移量,而与平台应用的对齐方式无关

对于sizeof字段,显然只需使用sizeof。这只会留下平台特定的问题——由于对齐,不同平台上的不同布局等-

编辑

这可能是一个愚蠢的问题,但为什么不直接将文件中的数据加载到struct的实例中,就像使用fwrite所做的那样,但反过来呢


您会遇到与上述相同的可移植性问题,这意味着如果使用不同的选项、不同的编译器或不同的平台重新编译代码,则代码可能无法读取自己的文件。但是对于一个单一平台的应用程序,这种方法非常有效。

对于您给出的示例,看起来您确实需要某种C解析器,它可以用您的类型声明解析字符串。然后您就能够以正确的方式解释从文件中读取的字节


C中的结构是按声明顺序在成员之间排列的。编译器可以根据平台特定的对齐需要在成员之间插入填充。变量的大小也是特定于平台的。

如果可以控制类,则可以使用成员指针。你绝对可以做到。问题是你是否应该


1以文本模式打开文件,并使用二进制输出函数fwrite。小心,这在某些平台上有效,但在其他平台上无效。2类的成员在默认情况下是私有的,您要么使用结构,要么将它们声明为公共的。@Arak+1用于指出错误;这更好吗?这是我用过的解决方案。如果你知道这种方法的局限性,这是最简单的方法。
class Metadata
{
public:
    virtual int getOffset() = 0;
};

template <typename THost, typename TField>
class TypedMetadata : Metadata
{
private:
    TField (THost::*memberPointer_);

    TypedMetadata(TField (THost::*memberPointer))
    {
        memberPointer_ = memberPointer;
    }

public:
    static Metadata* getInstance(TField (THost::*memberPointer))
    {
        return new TypedMetadata<THost, TField>(memberPointer);
    }

    virtual int getOffset()
    {
        THost* host = 0;

        int result = (int)&(host->*memberPointer_);

        return result;
    }
};

template<typename THost, typename TField>
Metadata* getTypeMetadata(TField (THost::*memberPointer))
{
    return TypedMetadata<THost, TField>::getInstance(memberPointer);
}

class Contained
{
    char foo[47];
};

class Container
{
private:
    int x;
    int y;
    Contained contained;
    char c1;
    char* z;
    char c2;

public:
    static Metadata** getMetadata()
    {
        Metadata** metadata = new Metadata*[6];

        metadata[0] = getTypeMetadata(&Container::x);
        metadata[1] = getTypeMetadata(&Container::y);
        metadata[2] = getTypeMetadata(&Container::contained);
        metadata[3] = getTypeMetadata(&Container::c1);
        metadata[4] = getTypeMetadata(&Container::z);
        metadata[5] = getTypeMetadata(&Container::c2);

        return metadata;
    }
};

int main(array<System::String ^> ^args)
{
    Metadata** metadata = Container::getMetadata();

    std::cout << metadata[0]->getOffset() << std::endl;
    std::cout << metadata[1]->getOffset() << std::endl;
    std::cout << metadata[2]->getOffset() << std::endl;
    std::cout << metadata[3]->getOffset() << std::endl;
    std::cout << metadata[4]->getOffset() << std::endl;
    std::cout << metadata[5]->getOffset() << std::endl;

    return 0;
}