views:

248

answers:

7

My application uses a large amount of Panda objects. Each Panda has a list of Bamboo objects. This list does not change once the Panda is initialized (no Bamboo objects are added or removed). Currently, my class is implemented as follows:

class Panda
{
    int a;
    int b;
    int _bambooCount;
    Bamboo* _bamboo;

    Panda (int count, Bamboo* bamboo)
    {
    _bambooCount = count;
    _bamboo = new Bamboo[count];

    // ... copy bamboo into the array ...
    }
}

To alleviate the overhead of allocating an array of Bamboo objects, I could implement this class as follows -- basically, instead of creating objects via the regular constructor, a construction method allocates a single memory block to hold both the Panda object and its Bamboo array:

class Panda
{
    int a;
    int b;

    Panda ()
    {
        // ... other initializations here ...
    }

    static Panda *createPanda (int count, Bamboo* bamboo)
    {
    byte* p = new byte[sizeof(Panda) +
     sizeof(Bamboo) * count];
    new (p) Panda ();

    Bamboo* bamboo = (Bamboo*)
     p + sizeof(Panda);

    // ... copy bamboo objects into the memory
        // behind the object...

        return (Panda*)p; 
    }
}

Can you foresee any problems with the second design, other than the increased maintenance effort? Is this an acceptable design pattern, or simply a premature optimization that could come back to bite me later?

+1  A: 

You use "new" look of new operator. It is fully correct relative Panda, but why don't you use Bamboo initializer?

Dewfy
+4  A: 

You'll be bitten if someone takes a Panda by value e.g.

//compiler allocates 16-bytes on the stack for this local variable
Panda panda = *createPanda(15, bamboo);

It may be acceptable (but is very probably a premature and horrible optimization) if you only ever refer to things by pointer and never by value, and if you beware the copy constructor and assignment operator.

ChrisW
+3  A: 

Based on my experience, premature optimization is most always "premature".. That is to say you should profile your code and determine whether or not there is a need for optimization or you are just creating more work for yourself in the long run.

Also, it seems to me that the questions as to whether the optimization is worth it or not depends a lot on the size of the Bamboo class and the average number of Bamboo objects per Panda.

Miky Dinescu
+6  A: 

C++ gives you another option. You should consider using std::vector.

class Panda
{
    int a;
    int b;
    std::vector<Bamboo> bamboo;
    // if you do not want to store by value:
    //std::vector< shared_ptr<Bamboo> > bamboo;

    Panda (int count, Bamboo* bamb) : bamboo( bamb, bamb+count ) {}
}

If you want to store Panda and Bamboos in continuous memory you could use solution from this article. The main idea is to overload operator new and operator delete.

Kirill V. Lyadvinsky
You must be aware of the side effects of this type of solutions and the problems you will have to deal with: users won't be able to use the objects inside containers (notably std::vector that preallocates memory based on the size of the type passed in) This trickery will endup with code that is not only hard to maintain (the questioner knows) but unnatural to use
David Rodríguez - dribeas
The most natural is to use `std::vector`. All other solutions has very limited use and should be applied with caution.
Kirill V. Lyadvinsky
If I'm not mistaken GCC's string implementation uses this kind of trickery. The trick is that the Panda object might contain a single pointer to the (overallocated) implementation class.
UncleBens
+3  A: 

This was find in C.
But in C++ there is no real need.

The real question is why do you want to do this?

This is a premature optimization, just use a std::vector<> internally and all your problems will disappear.

Because you are using a RAW pointer internally that the class owns you would need to override the default versions of:

  • Default Constructor
  • Destructor
  • Copy Constructor
  • Assignment operator
Martin York
+4  A: 

How do we convince people that in programming simplicity and clarity --in short: what mathematicians call 'elegance'-- are not a dispensable luxury, but a crucial matter that decides between success and failure?

-- Edsger W. Dijkstra

David Rodríguez - dribeas
Just don't. Many things can go wrong... how are you going to implement copy constructors, assignment, destructors? How are you going to deal with users that want to have arrays of Panda?
David Rodríguez - dribeas
+1  A: 

If you're that desperate, you can probably do something like this:

template<std::size_t N>
class Panda_with_bamboo : public Panad_without_bamboo
{
    int a;
    int b;
    Bamboo _bamboo[N];
}

But I believe you're not desperate, but optimizing prematurely.

sbi