Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm creating a couple of classes Vector2 (X & Y) and Vector3 (X, Y & Z), but I don't know whether to make Vector3 inherit from Vector2, or whether to re-implement the member variables m_x and m_y again? What are the pros and cons of each side (inheritance vs redefinition).

Edit: I'm using C++ (VS2010).

share|improve this question
1  
Why don't write a general vector class for n dimensional vectors and then (if needed) inherit a vector2 and a vector3 class. You may also use templates for the general class and inherit versions for integer vectors and float vectors, too. Edit: Why don't you use a optimized math library? –  danijar Oct 16 '12 at 21:07
5  
By no stretch of the imagination "Vector3 is a Vector2", they could both inherit from a parent VectorN though –  wim Oct 17 '12 at 0:00
1  
Yeah, but then you'll probably need a virtual table and that's one of the cases where the runtime & memory costs can matter. Ideally, a Vector3 should be just 3 floats as far as the memory is concerned. Not saying that's impossible, just that I've never seen that in a production engine. –  Laurent Couvidou Oct 17 '12 at 8:12
2  
Yes, I do think so. Until as long as you don't need anything else that floats. You know, YAGNI, KISS, all that stuff. Vector2, Vector3 and Vector4 with no inheritance and floats only is really the de facto standard in game engines. –  Laurent Couvidou Oct 17 '12 at 9:19
1  
I hope you meant typedef float real; ;). –  Mark Ingram Oct 17 '12 at 15:46
show 11 more comments

6 Answers

up vote 45 down vote accepted

No it shouldn't. The only thing you'd be using from the inheritance is the x and y components. The methods used in a Vector2 class wouldn't be useful in a Vector3 class, they would likely take different arguments and perform operations on a different number of member variables.

share|improve this answer
    
+1, I should pay more attention to the popup so I don't write redundant stuff. –  Matsemann Oct 16 '12 at 18:55
8  
Classical inheritance overuse. A Vector3 IS-NOT-A Vector2 (so it should not inherit), but an Apple IS-A Fruit (so it may inherit). If you twist your mind enough, a Vector3 HAS-A Vector2 in it, but the performance loss and difficulty coding means you will write completely separate classes for Vector3 and Vector2. –  bobobobo Oct 17 '12 at 15:20
    
But you could (an in my opinion should) write a n-dimensional vector class to inherit a 2d vector and a 3d vector from that. –  danijar Nov 11 '12 at 18:21
add comment

There's a curious thing you can do with C++ (You didn't specify a language, and this answer is mostly because I think it's nice to see alternatives, though I don't really believe this is useful in most cases.)

Using templates you can do something like this:

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

and this can be used like this:

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

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

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

Like I said, I don't think this is useful, and it will probably complicate your life when you try to implement dot/normalize/other stuff and try to be generic with any number of vectors.

share|improve this answer
    
Yeah, all the genericness seems nice, only most of the time you only need a standard 3-vector with 3 floating point components -- all the angle brackets will make Vector3f v quite a bit more bloaty Vector3<float> v –  bobobobo Oct 17 '12 at 14:55
    
@bobobobo Yes, I agree. My vector classes are usually vec2 and vec3 with no parent, but still make them templates. If writing Vector3<float> bothers you, you can always typedef it –  Luke B. Oct 17 '12 at 15:11
    
..And now the C programmer's argument.. "but what about increased compile time for using templates??" Is it really worth it in this case? –  bobobobo Oct 17 '12 at 15:17
    
@bobobobo I never had any problem with my compile times :P, but I never worked on a project that compile time would be an issue. One could argue that the compile times justify not using floats when you need integers. –  Luke B. Oct 17 '12 at 15:24
    
@bobobobo With explicit instantiations and not including your inline file in the header, compile times will be no different. Additionally, the template angle bracket bloat is just one typedef away. –  Samaursa Dec 26 '12 at 13:43
add comment

Regardless of speed, the first question you should ask yourself when doing any inheritance is if you're going to be using them polymorphically. More specifically, is there any situation where you can see yourself using a Vector3 as if it were a Vector2 (which, by inheriting from it, you're explicitly saying that a Vector3 "is-a" Vector2).

If not, then you shouldn't use inheritance. You shouldn't be using inheritance to share code. That's what components and external functions are for, not that you'd be sharing any code between them anyway.

That being said, you might want easy ways to convert Vector3s to Vector2s, and in that case you can write an operator overload that will implicitly truncate the Vector3 to a Vector2. But you shouldn't inherit.

share|improve this answer
    
Thanks, I think that's highlighted the problem, I was looking at this from a "code sharing" point of view (i.e. not having to "re-type" the X & Y values). –  Mark Ingram Oct 17 '12 at 15:05
    
+1 great answer, there's no polymorphic use between vectors of different sizes. –  Luke B. Oct 17 '12 at 15:26
    
This is the biggest thing I was going to add to my own answer - +1 for sure. (Although there are strange circumstances in which I can imagine wanting polymorphism - e.g., 2.5d 'heightmap' games where things like distance checks, pathing etc. will canonically want to be done in 2d but you still need to provide 3d coordinates for objects) –  Steven Stadnicki Oct 17 '12 at 21:30
    
@LukeB. Although in OPs case I agree that it seems there is no reason to inherit from Vector2 but inheriting from a base Vector<N>? That makes perfect sense. Moreover, why does inheritance automatically mean polymorphic behaviour? One of the best things about C++ is that you can have zero cost inheritance. No need to add any virtual methods (including virtual destructors) in the base Vector<N> class. –  Samaursa Dec 26 '12 at 13:46
add comment

No, since every method will need to be overridden as well you will have no use of actually inheriting from it.

If something, they could both implement a Vector interface. However, since you probably don't want to add/sub/dot/dst between a Vector2 and Vector3 this will have unwanted side effects. And having different parameters etc. would be a hassle.
So I really cannot see any pros of inheritance/interface in this case.

An example is the Libgdx framework, where Vector2 and Vector3 have nothing to do with each other, other than having same type of methods.

share|improve this answer
    
+1 for example links –  Byte56 Oct 16 '12 at 20:40
add comment

If you plan to use SIMD arrays are likely the best. If you still wish to use operator overloading you can consider using an interface/mixin to access the underlying array - for example, here is a starting point that has only the (untested) Add.

Notice how I haven't provided X/Y/Z, each VectorX class would inherit directly from this one - for the same reasons specified by other people. Still, I have seen arrays used as vectors many times in the wild.

#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);
    }
};

Disclaimer: My C++ might suck, it's been a while since I used it.

share|improve this answer
    
Wait man, does your use of _aligned_malloc mean the bug I opened isn't really a bug? –  bobobobo Oct 17 '12 at 14:58
    
You shouldn't use pointer casts to get your values into the __m128 register, you should use _mm_loadu_ps instead. A good sample class is here under "vectorclass.zip" –  bobobobo Oct 17 '12 at 15:02
    
@bobobobo I'll make a best-attempt at an edit - take special heed to the disclaimer ;). –  Jonathan Dickinson Oct 18 '12 at 16:40
    
@bobobobo _mm_loadu_ps should work out for you with that struct (where _mm_load_ps won't). I also added your suggestion - feel free to edit the question if you feel that I am barking up the wrong tree (it has been a while since I have used C[++]). –  Jonathan Dickinson Oct 18 '12 at 16:50
add comment

Another serious con to having Vec3 inherit from Vec2 or, arguably, to having both inherit from a single Vector class: your code will be doing a lot of operations on vectors, often in time-critical situations, and it's very much in your best interests to make sure that all of those operations are as fast as they can be - much more so than it is for many other objects that aren't quite so universal or low-level. While a good compiler will do its best to flatten out any inheritance overhead, you're still relying more on the compiler there than you'd like to; instead, I would build them as structs with as little overhead as possible and possibly even try and make most of the functions that use them (with the exception of things like operator+ which can't really be helped) be globals rather than methods on the struct. Early optimization is generally recommended against, and with excellent reason, but in a circumstance like this where you can be sure that vector operations will almost certainly be a substantial chunk of your runtime, it's worth a bit of effort up front to design for efficiency.

share|improve this answer
1  
-1 because: class and struct only have access grant implications in C++ and the OP didn't specify a language anyhow, non-virtual member functions have the same performance implications as non-member functions, and virtual member functions (which do potentially exhibit the issues you're concerned about) only exist if you make them, not simply by utilizing inheritance. –  Josh Petrie Oct 16 '12 at 19:42
2  
@JoshPetrie Valid points on all fronts; I tend to 'default' to C/C++ and so saw the question through that lens. I do believe there are legitimate performance (as well as conceptual) reasons for not going the inheritance route, mind you, but I could have been much better on the specific details. I'll try and revisit this and see if I can give a better accounting. –  Steven Stadnicki Oct 16 '12 at 20:23
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.