10

fwrite を使用して、オブジェクトをシーケンシャル ファイルに書き込みたいと考えています。クラスは次のようなものです

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

オブジェクトをファイルに書き込むとき。fwrite( this, sizeof(int), 2, fo)最初の 2 つの整数を書き込むために使用できるものを探しています。

問題は、オブジェクトの先頭に仮想テーブルが存在する可能性がある場合でも、オブジェクトデータthisの先頭を指すことが保証されていることです。したがって、上記の操作は安全です。

4

4 に答える 4

5

thisobjectのアドレスを提供しますが、これは必ずしも最初のメンバーのアドレスではありません。唯一の例外は、いわゆる標準レイアウトタイプです。C++11 標準から:

(9.2/20) を使用して適切に変換された標準レイアウト構造体オブジェクトへのポインターは、reinterpret_castその最初のメンバー (または、そのメンバーがビットフィールドの場合は、それが存在するユニット) を指し、その逆も同様です。[ 注: したがって、標準レイアウトの構造体オブジェクト内に名前のないパディングがある場合がありますが、適切な配置を実現するために必要な場合は、先頭にはありません。— エンドノート]

これは、標準レイアウト タイプの定義です。

(9/7) 標準レイアウト クラスとは、次のようなクラスです。
— 非標準レイアウト クラス (またはそのような型の配列) または参照の非静的データ メンバーを
持たない — 仮想関数 (10.3) を持たず、仮想基底クラスがない (10.1)
— すべての非静的データ メンバーに対して同じアクセス制御 (条項 11) がある
— 非標準レイアウトの基底クラス
がない — ほとんどの派生に非静的データ メンバーがないクラスと、非静的データ メンバーを持つ最大 1 つの基本クラス、または非静的データ メンバーを
持つ基本クラスがなく、かつ — 最初の非静的データ メンバーと同じ型の基本クラスがない。[108]

[108]これにより、同じクラス型を持ち、同じ最派生オブジェクトに属する 2 つのサブオブジェクトが同じアドレス (5.10) に割り当てられないことが保証されます。

オブジェクト タイプが POD である必要はないことに注意してください。上記で定義した標準レイアウトで十分です。(POD はすべて標準レイアウトですが、さらに、簡単に構築可能で、簡単に移動でき、簡単にコピーできます。)

あなたのコードからわかる限り、あなたのタイプは標準レイアウトのようです (アクセス制御がすべての非静的データ メンバーに対して同じであることを確認してください)。この場合、this実際に最初のメンバーを指します。シリアライゼーションの目的でこれを使用することに関して、標準では実際に明示的に次のように述べています。

(9/9) [ 注: 標準レイアウト クラスは、他のプログラミング言語で記述されたコードとの通信に役立ちます。それらのレイアウトは 9.2 で指定されています。— エンドノート]

もちろん、これでシリアル化の問題がすべて解決するわけではありません。特に、シリアル化されたデータの移植性は得られません (たとえば、エンディアンの非互換性のため)。

于 2013-05-29T07:16:10.710 に答える
5

いいえ、ちがいます。を使用できますfwrite(&a, sizeof(int), 2, fo)が、どちらも使用しないでください。特定のメモリ レイアウトに依存するべきではないため、安全性に関して言えば、生のメモリをただぶらぶらすることはほとんど良い考えではありません。誰かがあなたのコードを壊していることに気づかずに、と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 に答える