My C++ Class Idiom Gotcha

Published 2011-11-16 on Farid Zakaria's Blog

Idiom: Classes

Definitely I get trapped into a C++ style of thinking; or rather an object-oriented style of thinking. I'm still a fan of object oriented programming (although I have read post after post about how wonderful functional programming is), however I've ran into a gotcha at work because of it.


"gotcha" has become a term for a feature of a programming language that is likely to play tricks on you to display behavior that is different than what you expect.

What I wanted to do

We had a very small structure representing a 4D float Vector. I began to use it however decided that I wanted to add a few more methods, a constructor etc... Therefore I decided to turn my nice lean small structure into a Class!


struct Vector
{
float x;
float y;
float z;
float w;
};

became...


class Vector4f
{
public:
    union
    {
        struct  
        {
            float x;
            float y;
            float z;
            float w;
        };
        struct 
        {
            float X;
            float Y;
            float Z;
            float W;
        };
        FLOAT data[4];
    };
    Vector4f();
    Vector4f(float x, float y, float z, float w);
    virtual ~Vector4f() {}

    static Vector4f Zero() { return m_zeroVector; }

    Vector4f Add(const Vector4f & vector2) const;
    Vector4f operator+(const Vector4f & vector2) const;
    float & operator[](unsigned int component);
    float const & operator[](unsigned int component) const;
private:
    const static Vector4f m_zeroVector;
};

C++ is an object-oriented language, thus I have the tendency to wrap everything in a Class rather than a Struct so that I can extend them and it is more inline with the OO-idiom. This has generally been fine for most code I've written however my work has a lot of mixing between C and C++ coding (multimedia driver development for those curious).

My OO-idiom failed me with the use of memcpy. Many assignment operations in the codebase are done via memcpy instead of the assignment operator.


float data[4];
Vector4f vector4;
memcpy(&data, &vector4, sizeof(Vector4f)); //OUT OF BOUNDS

The culprit is line 24 in the definition of the Vector4f class. Adding virtual to a method, causes the class definition to contain an additional vbptr (virtual base pointer). It caused the size of the class to go from 16 to 20 (assuming 4byte pointer). It was from force of habit of always including virtual in the destructor, which is itself another gotcha!

Whether what I encountered is a valid gotcha or me just being an idiot inexperienced, I haven't decided. For now, I've added a static assert (compile time assert) to verify the sizeof the class is as expected in case someone in the future decides to redo my blunder.