设计Vector3D类

时间:2009-09-23 17:21:32

标签: c++ class

当我们想要创建一个新的矢量3D类时,我不知道哪种是最佳实践,我的意思是,这两个例子中哪一个是最好的方法?

class Vec3D
{
   private:

         float m_fX;
         float m_fY;
         float m_fZ;

...
};

class Vec3D
{
   private:

         float m_vVec[3];
...
};

使用第一个方法,我们有单独的变量,我们可以确保在内存中是连续的,因此缓存可能会失败,但访问这些变量只需一条指令。

对于第二个方法,我们在内存中有一个3个连续浮点数的向量,这里的高速缓存很好,但是每次访问都会产生一个额外的变量偏移量。 Buti认为这个载体aproach可以更好地适应像SSE2 / 3这样的优化。

哪种方法更好,我迷路了,我需要建议:)

感谢您的时间:)

LLORENS

13 个答案:

答案 0 :(得分:13)

使用

class Vec3D
{
   private:
    union 
    {
        float m_vVec[3];
        struct
        {
             float m_fX;
             float m_fY;
             float m_fZ;
        };
    };
    ...
}

这将为您提供无需额外费用

答案 1 :(得分:4)

第二个将使矩阵运算更简单。

在编写自己的向量和矩阵类之前,您可能需要查看openscengraph

之类的代码。

答案 2 :(得分:2)

我更喜欢第一种方法,因为它会让你的代码更具可读性。

答案 3 :(得分:2)

我会选择前者,主要是因为可读性。如果你有很多数组索引,很容易迷失在等式中,但是如果你明确地将它作为x,y,z,那么就更容易理解发生了什么。

答案 4 :(得分:2)

两种形式的内存布局都是相同的,除非你的编译器有一些非常奇怪的填充。如果成员变量在类中一起声明,它们将被放在一起。

我会根据将使用变量的代码的清晰度做出选择。

答案 5 :(得分:2)

假设您将使用向量进行计算密集型操作,我建议将您的成员公开(或者只使用结构而不是类)。你应该跳过getter的开销,直接访问vector成员。

就语法而言,第一种形式更具可读性。如果您需要以3个值的数组的形式访问成员,您还可以考虑使用一个为您提供单独成员和数组访问权限的联合。

答案 6 :(得分:1)

选项3?

struct Vec3D
{
    // ...
}

答案 7 :(得分:1)

你应该问自己更重要的问题是你需要通过索引来引用坐标,还是将它们称为x,y和z就足够了。

答案 8 :(得分:1)

我反对实现你自己的3D矢量类。

尽管这样的课程看起来很简单,但是需要大量的工作来制作这种高效,可靠,健壮和准确的课程。

没有什么比花费很多时间寻找一个奇怪的错误更糟糕了,但最终发现它是由你的矢量类中的数值不稳定导致某些特定输入的错误答案。相信我,我去过那里!

有许多库可供使用,经过数千年用户多年的尝试和测试,因此可以放心使用。

我成功使用多年的图书馆是Andrew Willmott的简单矢量图书馆。 http://www.cs.cmu.edu/~ajw/doc/svl.html

SVL简单明了。但是,它确实有一个非常老式的API,对我来说,问题是它是另一个需要链接并由我的客户加载的第三方库。

所以,最近,我一直在使用boost :: uBLAS,特别是基于此处描述的固定大小的矢量包装:http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Effective_UBLAS

当你第一次接近它时,升级库一如既往地令人生畏。但是,它非常高效,完整,积极维护,并且无论如何都将升级链接到您的程序中时“免费”。

答案 9 :(得分:0)

从性能的角度来看,编译器应该能够为两者发出同样高效的代码。但是,两者都有利有弊。

第一个肯定更具可读性。但是,第二个允许您通过索引获取变量。因此,如果您需要执行诸如将所有2个向量一起添加的操作,那么该代码将更简单:

for(int i = 0; i < 3; ++i) {
    vVec[i] += o.vVec[i];
}

VS

m_fX += o.m_fX;
m_fY += o.m_fY;
m_fZ += o.m_fZ;

所以我想说,根据你想要达到的目标,做任何你觉得更舒适和可读的东西。

答案 10 :(得分:0)

我肯定会选择第二种方法。在你的代码的许多地方,它将允许你有一个优雅的循环而不是长的重复。考虑一下:

for (int i = 0; i < 3; i++) {
  for (int j = 0; j < 3; j++) {
    VecMult.m_vVec[i] += Vec1.m_vVec[i] * Vec2.m_vVec[j];
}

对此:

  VecMult.m_fX = Vec1.m_fX * Vec2.m_fX + Vec1.m_fX * Vec2.m_fY + Vec1.m_fX * Vec2.m_fZ;
  VecMult.m_fY = Vec2.m_fX * Vec2.m_fX + Vec2.m_fX * Vec2.m_fY + Vec2.m_fX * Vec2.m_fZ;
  VecMult.m_fZ = Vec3.m_fX * Vec2.m_fX + Vec3.m_fX * Vec2.m_fY + Vec3.m_fX * Vec2.m_fZ;

答案 11 :(得分:0)

两者都有利弊。通常v1.X * v2.Yv1[0] * v2[1]更具可读性。但是有一些算法可以对矢量进行索引,第二种解决方案可以编写v1[a] * v2[b]。在这种情况下,第一个解决方案将调用丑陋的ifswitch块。我不是C ++程序员,但也许你可以通过使用宏或C ++支持来获得最好的两个(其他人建议的union似乎是一个很好的候选者)。在C#中,我将使用属性创建类似下面的内容。

public class Vector
{
    private readonly Single[] components = new Single[3];

    public Single X
    {
        get { return components[0]; }
        set { this.components[0] = value; }
    }

    public Single Y { ... }
    public Single Z { ... }
}

最后我相信性能不会有问题,因为索引可以由处理器的地址计算单元处理。

答案 12 :(得分:0)

我会使用第二种(数组)方法,因为它允许您使用STL算法来实现成员函数。

例如:

#include <cmath>
#include <numeric>

float Vec3D::Magnitude()
{
   return std::sqrt(std::inner_product(m_vVec, m_vVec + 3, m_vVec, 0.0f));
}