Vector3应该继承Vector2吗?


18

我创建了几个类别Vector2(X&Y)和Vector3(X,Y,Z),但我不知道是否进行Vector3继承Vector2,还是重新实现成员变量m_xm_y一次?双方的优缺点是什么(继承与重新定义)。

编辑:我正在使用C ++(VS2010)。


1
为什么不为n维向量编写一个通用向量类,然后(如果需要)继承一个vector2和一个vector3类。您也可以为通用类使用模板,也可以为整数向量和浮点向量继承版本。编辑:为什么不使用优化的数学库?
danijar

5
毫不夸张地说,“ Vector3是Vector2”,尽管它们都可以从父VectorN继承
2012年

1
是的,但是您可能需要一个虚拟表,这是运行时和内存成本很重要的情况之一。理想情况下,Vector3floats内存而言,a 应该仅为3 。并不是说这是不可能的,只是我从未在生产引擎中看到过。
劳伦·库维杜

2
是的,我确实这么认为。直到你不需要其他东西为止floats。你知道的,YAGNI,KISS,所有这些东西。Vector2Vector3并且Vector4没有继承关系,floats而实际上只是游戏引擎中的事实上的标准。
Laurent Couvidou 2012年

1
我希望你的意思是typedef float real;;)。
马克·英格拉姆

Answers:


47

不,不应该。从继承中唯一要使用的是xand y组件。Vector2类中使用的方法在类中不是有用的Vector3,它们可能采用不同的参数并对不同数量的成员变量执行操作。


+1,我应该更加注意弹出窗口,因此我不会写多余的东西。
Matsemann

8
古典继承过度使用。一个Vector3IS-NOT-A Vector2(因此它不应该继承),而是一种AppleIS-A Fruit(所以它可能继承)。如果你扭你的心就够了,一个Vector3HAS-A Vector2在里面,但性能损失和困难的编码方式你会写完全独立的类Vector3Vector2
bobobobo 2012年

但是,您可以(我认为应该这样做)编写一个n维向量类,以从中继承2d向量和3d向量。
danijar

8

使用C ++可以做一个奇怪的事情(您没有指定语言,并且这个答案主要是因为我认为很高兴看到替代方法,尽管我真的不认为这在大多数情况下很有用。)

使用模板,您可以执行以下操作:

template <class T, class S, int U>
class VectorN
{
    protected:
        int _vec[U];
    public:
        S& operator+=(const S c)
        {
            for(int i = 0; i < U; i++)
            {
                _vec[i] += c.at(i);
            }
            return (S&)*this;
        }
        int at(int n) const
        {
            return _vec[n];
        }
};

template <class T>
class Vec2 : public VectorN<T,Vec2<T>,2>
{
    public:
        T& x;
        T& y;
        Vec2(T a, T b) : x(this->_vec[0]), y(this->_vec[1])
        {
            this->_vec[0] = a;
            this->_vec[1] = b;
        }
};

template <class T>
class Vec3 : public VectorN<T,Vec3<T>,3>
{
    public:
        T& x;
        T& y;
        T& z;
        Vec3(T a, T b, T c) : x(this->_vec[0]), y(this->_vec[1]), z(this->_vec[2])
        {
            this->_vec[0] = a;
            this->_vec[1] = b;
            this->_vec[2] = c;
        }
};

可以这样使用:

int main(int argc, char* argv[])
{

    Vec2<int> v1(5,0);
    Vec2<int> v2(10,1);

    std::cout<<((v1+=v2)+=v2).x;
    return 0;
}

就像我说的那样,我认为这没有用,当您尝试实现点/归一化/其他内容并尝试与任意数量的向量通用时,这可能会使您的生活变得复杂。


是啊,所有的一般性词语似乎不错,只是你只需要3个浮点组件的标准3-矢量大部分时间-所有的尖括号会使Vector3f v相当多的bloatyVector3<float> v
bobobobo

@bobobobo是的,我同意。我的向量类通常是vec2和vec3,没有父类,但仍将它们作为模板。如果写的Vector3 <浮动>困扰你,你总是可以的typedef它
卢克B.

..现在是C程序员的论点..“但是增加使用模板的编译时间呢?” 在这种情况下真的值得吗?
bobobobo 2012年

@bobobobo我的编译时间:P从来没有任何问题,但我从未从事过编译时间会成为问题的项目。有人可能会争辩说,在需要整数时,编译时间证明不使用浮点数是合理的。
路加B.

@bobobobo具有显式实例化,并且头文件中不包含内联文件,因此编译时间没有什么不同。此外,模板尖括号膨胀仅一typedef处之遥。
Samaursa

7

无论速度如何,执行任何继承时应该问自己的第一个问题是,是否要多态使用它们。更具体地说,在任何情况下,您都可以看到自己Vector3好像使用a 一样Vector2(通过继承它,您是在明确地说Vector3是“ is-a” Vector2)。

如果没有,那么您不应该使用继承。您不应该使用继承来共享代码。那就是组件和外部功能的目的,而不是您要在它们之间共享任何代码。

话虽这么说,您可能需要简单的方法 Vector3 s 转换Vector2s,在这种情况下,您可以编写运算符重载,将其隐式截断Vector3Vector2。但是你不应该继承。


谢谢,我认为这突出了问题,我是从“代码共享”的角度来看的(即不必“重新键入” X&Y值)。
马克·英格拉姆

+1个好答案,不同大小的向量之间没有多态使用。
路加B.

这是我要添加到自己答案中的最大内容-肯定是+1。(虽然有奇怪的情况中,我想象想要多态性-例如,2.5D“高度图”游戏里的东西一样距离检查,寻路等将规范地想在2D做,但你仍然需要为对象提供三维坐标)
史蒂文·斯塔德尼基

@LukeB。尽管在OP中,我同意似乎没有理由要继承,Vector2而是要继承基数Vector<N>?这是很合理的。此外,为什么继承会自动意味着多态行为?关于C ++的最好的事情之一就是您可以实现零成本继承。无需在基类中添加任何虚拟方法(包括虚拟析构函数)Vector<N>
Samaursa

5

不可以,因为每个方法都需要被覆盖,因此您将无法实际使用它。

如果有的话,他们都可以实现Vector接口。但是,由于您可能不想在Vector2和Vector3之间添加/ sub / dot / dst,因此会有不良的副作用。并且具有不同的参数等将是麻烦的。
因此,在这种情况下,我真的看不到任何继承/接口的优点。

一个例子是Libgdx框架,其中Vector2Vector3除了具有相同类型的方法外,彼此无关。


2

如果您打算使用SIMD 阵列,则可能是最好的选择。如果仍然希望使用运算符重载,则可以考虑使用接口/混合来访问基础数组-例如,这里是一个仅包含(未测试)的起点Add

请注意我如何不提供X/ Y/ Z,每个VectorX班级将直接从这个继承-由其他人指定同样的原因。不过,我在野外多次看到数组用作向量。

#include <xmmintrin.h>

class Vector
{
public:
    Vector(void)
    {
        Values = AllocArray();
    }

    virtual ~Vector(void) 
    { 
        _aligned_free(Values);
    }

    // Gets a pointer to the array that contains the vector.
    float* GetVector()
    {
        return Values;
    }

    // Gets the number of dimensions contained by the vector.
    virtual char GetDimensions() = 0;

    // An example of how the Vector2 Add would look.
    Vector2 operator+ (const Vector2& other)
    {
        return Vector2(Add(other.Values));
    }

protected:
    Vector(float* values)
    {
        // Assume it was created correctly.
        Values = values;
    }

    // The array of values in the vector.
    float* Values;

    // Adds another vector to this one (this + other)
    float* Add(float* other)
    {
        float* r = AllocArray();

#if SSE
        __m128 pv1 = _mm_load_ps(Values);
        __m128 pv2 = _mm_load_ps(other);
        __m128 pvr = _mm_load_ps(r);

        pvr = _mm_add_ps(pv1, pv2);
        _mm_store_ps(r, pvr);

#else
        char dims = GetDimensions();
        for(char i = 0; i < dims; i++)
            r[i] = Values[i] + other[i];
#endif

        return r;
    }

private:

    float* AllocArray()
    {
        // SSE float arrays need to be 16-byte aligned.
        return (float*) _aligned_malloc(GetDimensions() * sizeof(float), 16);
    }
};

免责声明:我的C ++可能很烂,自使用以来已经有一段时间了。


伙计,您使用的_aligned_malloc意思是我打开错误不是真的错误吗?
bobobobo 2012年

您不应该使用指针强制转换将值放入__m128寄存器,_mm_loadu_ps而应该使用。一个好的样本类是这里 “vectorclass.zip”下
bobobobo

@bobobobo我将在编辑时尽力尝试-特别注意免责声明;)。
乔纳森·迪金森

@bobobobo _mm_loadu_ps应该为您解决该结构(如果_mm_load_ps没有)。我还添加了您的建议-如果您感觉到我在树上叫错了,请随时编辑问题(自从我使用C [++]以来已经有一段时间了)。
乔纳森·迪金森

1

从Vec2继承Vec3的另一个严重缺点,或者可以说,从一个Vector类继承这两者是另一个严重的缺点:您的代码将做很多事情向量,通常在时间紧迫的情况下进行,并且确保所有这些操作尽可能的快是非常符合您的最大利益的,这要比对许多其他非对象的操作要快得多非常普遍或低级。一个好的编译器会尽力消除所有继承开销,但是您仍然比那里想要的更多地依赖编译器。相反,我会以尽可能少的开销将它们构建为结构,甚至可能尝试使使用它们的大多数功能(除了operator +之类的东西无法真正帮助)是全局的,而不是全局的方法。结构。通常建议针对优化,并且有充分的理由,


1
-1,因为:类和结构仅在C ++中具有访问授予的含义,并且OP无论如何都没有指定语言,非虚拟成员函数与非成员函数和虚拟成员函数(可能确实表现出相同的性能暗示) (您关心的问题)仅在创建时存在,而不仅仅是利用继承。

2
@JoshPetrie所有方面的有效点;我倾向于将“默认”设置为C / C ++,因此从这个角度看了这个问题。我确实相信,请注意,出于合理的性能(以及概念上)的原因,不走继承路线,但是在具体细节上我可能要好得多。我将尝试重新进行检查,看看是否可以提供更好的会计处理。
史蒂文·斯塔德尼基
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.