C++ 设计一个Vector3D类

C++ 设计一个Vector3D类,c++,class,C++,Class,我不知道当我们想要创建一个新的vector 3D类时,哪一个是最好的实践,我的意思是,这两个例子中哪一个是最好的方法 class Vec3D { private: float m_fX; float m_fY; float m_fZ; ... }; 或 对于第一个aproach,我们有单独的变量,我们不能确保在内存中是连续的,因此缓存可能会失败,但是对这些变量的访问是一条指令 对于第二个aproach,内存中有一个由3个连续浮点组

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

class Vec3D
{
   private:

         float m_fX;
         float m_fY;
         float m_fZ;

...
};

对于第一个aproach,我们有单独的变量,我们不能确保在内存中是连续的,因此缓存可能会失败,但是对这些变量的访问是一条指令

对于第二个aproach,内存中有一个由3个连续浮点组成的向量,这里的缓存很好,但是每次访问都会产生一个额外的变量偏移量和。但我认为这种向量方法更适合SSE2/3之类的优化

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

谢谢您的时间:)

LLOREN

选项3

struct Vec3D
{
    // ...
}

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

我会选择前一种方法,主要是因为可读性。如果有很多数组索引,很容易在等式中迷失方向,但如果将其显式地表示为x、y、z,则更容易理解发生了什么。

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


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

从性能的角度来看,编译器应该能够为这两个变量发出同样有效的代码。然而,两者都有利弊

第一个肯定更具可读性。但是,第二种方法可以通过索引获取变量。因此,如果您需要将所有2个向量相加,那么代码会更简单:

for(int i = 0; i < 3; ++i) {
    vVec[i] += o.vVec[i];
}
所以我想说,根据你想要实现的目标,做任何你觉得更舒服和可读的事情。

使用

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

这将使你们两个都不需要额外的费用

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

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];
}

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


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

两者都有利弊。通常

v1.X*v2.Y
v1[0]*v2[1]
更具可读性。但也有一些算法对向量进行索引,第二种解决方案允许写入
v1[a]*v2[b]
。在这种情况下,第一个解决方案将调用丑陋的
if
开关
块。我不是C++程序员,但也许你可以通过使用宏或者C++支持的任何东西(如<代码>联合/代码>其他人建议的那样,是一个很好的候选对象)来获得最好的结果。在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 { ... }
}

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

第二个将使矩阵操作更简单


在编写自己的向量和矩阵类之前,您可能希望查看代码中的类似内容,例如

您应该问自己的更重要的问题是,您是否需要通过索引引用坐标,或者称它们为x、y和z就够了

我反对实现您自己的3D向量类

尽管这样一个类似乎足够简单,但要创建一个高效、可靠、健壮和准确的此类类,需要进行大量的工作

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

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

我多年来成功使用的一个库是Andrew Willmott的简单向量库

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

因此,最近,我一直在使用boost::uBLAS,特别是基于以下描述的固定大小向量包装器:

与往常一样,当您第一次接触boost库时,它是令人生畏的。但是,它非常高效、完整,并且可以主动维护,并且在boost链接到您的程序时是免费的。

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

例如:

#include <cmath>
#include <numeric>

float Vec3D::Magnitude()
{
   return std::sqrt(std::inner_product(m_vVec, m_vVec + 3, m_vVec, 0.0f));
}
#包括
#包括
float Vec3D::magnity()
{
返回标准::sqrt(标准::内部产品(m_vVec,m_vVec+3,m_vVec,0.0f));
}

这肯定是过早优化的情况……您确定结构的相邻部分在内存中不一定相邻吗?第二个for循环的目标是什么?;)这看起来像是复制粘贴引起的错误。这是我在C++中用3D方法表示的一个3D向量。当我使用这个方法时,要知道匿名结构是编译器扩展,所以确保你对你的平台有支持。VMMLIB是用这种方式写的,便于OpenGL使用。谢谢你的评论,我会考虑你的建议:)@slashmais:你的代码利用编译器扩展(匿名结构)并邀请利用未定义的行为(从非活动工会成员处读取)。值小于0,因此为-1。我不确定是否存在
  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;
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 { ... }
}
#include <cmath>
#include <numeric>

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