In the previous tutorial we moved our complex data into the queue by copy()ing it directly into the message block's data area. I hope that most readers got a queasy feeling when I did that. It just isn't a good idea...
A better idea would be to teach the message queue about our data types (or at least a baseclass) so that it can more efficiently handle things:
// page03.html,v 1.10 2000/03/19 20:09:26 jcej Exp #ifndef BLOCK_H #define BLOCK_H #include "work.h" /* We derive a Message_Block from ACE_Message_Block and teach it about our Unit_Of_Work object. When our task's svc() method pulls a block out of the queue, it can then invoke the virtual methods of the work object safely. In this implementation we've also retained the original ACE_Message_Block functionallity so that we can use the underlying ACE_Data_Block objects to store data other than our Unit_Of_Work. */ class Message_Block : public ACE_Message_Block { public: typedef ACE_Message_Block inherited; /* Construct our underlying ACE_Message_Block with the requested data size and initialize our Unit_Of_Work pointer with the given object instance. Note that this Message_Block instance now assumes ownership of the Unit_Of_Work and will delete it when the Message_Block is deleted. */ Message_Block( size_t size, Unit_Of_Work * _data ) : inherited(size), data_(_data) { ACE_DEBUG ((LM_DEBUG, "(%P|%t) Message_Block ctor 0x%x for 0x%x\n", (void *) this, data_)); } ~Message_Block(void) { ACE_DEBUG ((LM_DEBUG, "(%P|%t) Message_Block dtor 0x%x for 0x%x\n", (void *) this, data_)); delete data_; } /* Return the Unit_Of_Work so that the task can invoke methods on it. */ Unit_Of_Work * data(void) { return this->data_; } protected: Unit_Of_Work * data_; /* Disallow these very dangerous operations. If we were to copy a Message_Block object then the data_ pointer would get copied and we would eventually end up deleting the same object multiple times! That's not good. By preventing the copy, we can avoid this. */ Message_Block &operator= (const Message_Block &); Message_Block (const Message_Block &); }; #endif
Ok, this looks pretty good. We just construct our specialized Message_Block instead of the generic ACE_Message_Block and let it carry our data along. When our application is done with the message block and release()es it, we know that our work object will also be taken care of.
Let's now go to main() and see what we had to change there to use this specialization.