10

我想使用 fwrite 将对象写入顺序文件。班级就像

class A{
    int a;
    int b;
public:
    //interface
}

当我将对象写入文件时。我在徘徊,我可以用它fwrite( this, sizeof(int), 2, fo)来写前两个整数。

问题是:this保证指向对象数据的开始即使有可能有一个虚拟表存在于对象的最开始。所以上面的操作是安全的。

4

4 回答 4

5

this提供对象的地址,不一定是第一个成员的地址。唯一的例外是所谓的标准布局类型。来自 C++11 标准:

(9.2/20) 指向标准布局结构对象的指针,使用reinterpret_cast,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。[注意:因此,标准布局结构对象中可能存在未命名的填充,但不是在其开头,这是实现适当对齐所必需的。——尾注]

这是标准布局类型的定义:

(9/7) 标准布局类是这样的类:
— 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
— 没有虚函数 (10.3) 和没有虚拟基类 (10.1),
— 对所有非静态数据成员具有相同的访问控制(第 11 条),
— 没有非标准布局的基类,
— 在最派生的部分中没有非静态数据成员类和最多一个具有非静态数据成员的基类,或者没有具有非静态数据成员的基类,并且
- 没有与第一个非静态数据成员相同类型的基类。[108]

[108]这确保了具有相同类类型且属于相同最派生对象的两个子对象不会分配在相同地址(5.10)。

请注意,对象类型不必是 POD - 具有上面定义的标准布局就足够了。(POD 都有标准布局,但除此之外,它们还可以简单地构造、简单地移动和简单地复制。)

据我从您的代码中可以看出,您的类型似乎是标准布局(确保所有非静态数据成员的访问控制相同)。在这种情况下,this确实会指向初始成员。关于将其用于序列化目的,标准实际上明确表示:

(9/9) [注意:标准布局类对于与用其他编程语言编写的代码进行通信很有用。它们的布局在 9.2 中指定。——尾注]

当然这并不能解决序列化的所有问题。特别是,您将无法获得序列化数据的可移植性(例如,由于字节顺序不兼容)。

于 2013-05-29T07:16:10.710 回答
5

不,这不对。你可以使用fwrite(&a, sizeof(int), 2, fo),但你也不应该使用。在安全方面,仅仅浏览原始内存很少是一个好主意,因为您不应该依赖特定的内存布局。有人可以在 and 之间引入另一个变量,c而不会注意到他正在破坏您的代码。如果要访问变量,请明确执行。不要只访问您认为变量所在的内存或上次检查时它们所在的内存。ab

于 2013-05-29T06:58:38.447 回答
1

许多答案正确地说“不”。下面是一些代码,演示了为什么this永远不能保证指向对象的开头:

#include <iostream>

class A {
    public: virtual int value1() { std::cout << this << "\n"; }
};

class B {
    public: virtual int value2() { std::cout << this << "\n"; }
};

class C : public A, public B {};

int main(int argc, char** argv) {
    C* c = new C();
    A* a = (A*) c;
    B* b = (B*) c;
    a->value1();
    b->value2();
    return 0;
}

请注意this虚方法中的使用。

输出可以(取决于编译器)向您显示指针ab不同。很可能,a将指向对象的开始,但b不会。当使用多重继承时,这个问题最容易出现。

于 2013-05-29T07:08:13.247 回答
0

fwrite由于许多原因,使用将对象写入文件是一个非常糟糕的主意。例如,如果您的类包含一个,std::vector<int>您将保存指向整数的指针,而不是整数。

出于“更高级别”的原因(对齐、版本控制、二进制兼容性),即使在 C 中,即使成员只是简单的本机类型,在大多数情况下也是一个坏主意。

于 2013-05-29T07:04:57.533 回答