5

float の 3D ベクトルを表すクラスがあります。

class Vector3D
{
    public:

    float x, y, z;
    float * const data;

    Vector3D() : x(0.0), y(0.0), z(0.0), data(&x) {}
}

私の質問は次のとおりです: x、y、および z は、x のアドレスをデータに割り当て、後でデータに対して添字演算子を使用して配列としてベクトル コンポーネントにアクセスできるように、メモリに順次割り当てられますか?

たとえば、ベクター コンポーネントに直接アクセスしたい場合があります。

Vector3D vec;
vec.x = 42.0;
vec.y = 42.0;
vec.z = 42.0;

また、オフセットでアクセスしたい場合もあります。

Vector3D vec;
for (int i = 3; i--; )
    vec.data[i] = 42.0;

2 番目の例は最初の例と同じ効果がありますか、それとも x、y、z フロート以外のメモリを上書きするリスクがありますか?

4

6 に答える 6

6

いいえ、これは未定義の動作です。理由は 2 つあります。

  • まず、他の誰もが言及したパディングの問題について。
  • 第 2 に、たとえ正しくパディングされたとしても、ポインターが指している範囲を超えてしまうようなオフセットでポインターを逆参照することは有効ではありません。コンパイラはこれを自由に想定し、違反した場合に未定義の動作につながる最適化を行います。

ただし、次の場合は有効です。

class Vector3D
{
public:
    std::array<float,3> data;
    float &x, &y, &z;

    Vector3D() : data(), x(data[0]), y(data[1]), z(data[2]) { }
    Vector3D& operator =(Vector3D const& rhs) { data = rhs.data; return *this; }
};

std::arrayは C++0x の新機能であり、基本的には と同等boost::arrayです。C++0x や Boost が必要ない場合は、a を使用できますstd::vector(そしてイニシャライザを に変更しますdata(3))。これははるかに重いソリューションですが、そのサイズは外部から変更できます。結果はUBになります。

于 2011-06-07T23:37:28.437 に答える
2

はい。このクラスは、次の理由により、レイアウト互換の standard-layoutです。

  • 仮想機能がありません。
  • すべてのデータ メンバーは、単一のアクセス指定子ブロック ( public:)にあります。

このため、C 構造体のようにシーケンシャルに配置されることが保証されています。これにより、ファイル ヘッダーを構造体として読み書きできるようになります。

于 2011-06-07T23:49:08.690 に答える
1

コンパイラには、構造体内のメモリのレイアウト方法にある程度の柔軟性があります。構造体が別のデータ構造とオーバーラップすることはありませんが、要素間に未使用のスペースを挿入できます。指定した構造体では、一部のコンパイラは、データ ポインターを整列できるように、z とデータの間に 4 バイトの余分なスペースを追加することを選択する場合があります。ほとんどのコンパイラは、すべてを密にパックする方法を提供します。

編集: コンパイラが x、y、および z を密にパックすることを選択するという保証はありませんが、実際には、構造体の最初の要素であり、サイズが 2 のべき乗であるため、適切にパックされます。

于 2011-06-08T00:12:54.803 に答える
1

または operator[] オーバーロードを持つことができます

float operator[](int idx)
{
 switch (idx)
{
case 0:
  return x;
case 1:
  return y;
case 2:
 return z;
}
assert (false);
}
于 2011-06-08T00:19:13.683 に答える
1

あなたの解決策は有効ではありませんが、コンパイラが「正しいことを行う」ことを保証できる (または知っている) 場合 (特に、x、y、z 要素間のパディングを制御することによって) は問題ありません。この場合、dataメンバーを完全に削除して使用しますoperator[]

このようなものが使われているのを見たことがあります。まったく同じ問題が発生しますが、そのデータ ポインターを保存する手間が省け、より適切なv[0]構文を使用できますv.data[0]

class Vector3D
{
    public:

    float x, y, z;
    float& operator[](int i) { return *(&x+i); }
    const float& operator[](int i) const { return *(&x+i); }

    Vector3D() : x(0.0), y(0.0), z(0.0) {}
}

編集:ildjam heres によってプロンプトが表示され、メンバーではなくアクセサーを使用する準拠バージョンが表示されます。これは似ています。

class Vector3D
{
    public:
      float& operator[](int i) { return v[i]; }
      const float& operator[](int i) const { return v[i]; }

      float& x() { return v[0]; }
      float  x() const { return v[0]; }
      float& y() { return v[1]; }
      float  y() const { return v[1]; }
      float& z() { return v[2]; }
      float  z() const { return v[2]; }

      Vector3D() : v() {}
    private:    
      float v[3];
};
于 2011-06-08T00:27:49.733 に答える
-1

次のようにします。

float data[3];
float& x, y, z;

    Vector3D() : x(data[0]), y (data[1]), z(data[2]) { data [0] = data [1] = data [2] = 0;}
于 2011-06-07T23:44:51.190 に答える