RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Using a Good Parasite Class to Design a Self-Clearing Memory Buffer : Page 2

Your C++ application doesn't have to allocate a new chunk of memory on every loop iteration. Learn how to implement a single raw memory buffer that knows how to clear itself securely.


memset() Blues

Without STL containers getting in your way, the main processing loop might look like a good 1970s C textbook:

char sms_buffer[161]={0};
for(;;) //main processing loop
  memset(sms_buffer, 0, 161); //clear all bytes

Calling memset() directly in C++ code is a faux pas because passing the size of the buffer on every memset()call is a security hazard. Ideally, you want the buffer to clear itself.

Enter the Good Parasite class!

A Good Parasite

A proper solution to the self-clearing buffer requires an interface with the dual attributes of a low-level char array and the qualities of a high-level C++ class. Why use a class instead of a function? Because a class's constructor is invoked automatically, and that constructor is where the buffer clearing will take place.

How will that class keep track of the buffer's size and address? Simple: both the class and the buffer live at the same address! So you monitor the buffer's size by bundling the size into the class's type—that is, you use a template. Bundling the size into the specialization has another advantage: the class will not contain any data members at all. This is crucial for your design.

First, consider how to bundle the buffer's size into the class's type. This Solution uses a template non-type parameter:

#include <new> //for overriding new and placement new
#include <cstring> //for memset

template <int N> class Zeroer//template arguments are int
 void * operator new(unsigned int)=delete;
 Zeroer(const Zeroer&)=delete;
 Zeroer& operator=(const Zeroer&)=delete;

Because clients are not allowed to create Zeroer objects directly or to copy them, the assignment operator, copy constructor, destructor, and overridden operator new are declared as private deletedfunctions.

You can instantiate a Zeroer object only by using placement new. This will ensure that a Zeroer object is constructed on the buffer's address. Notice that you need to override the global placement new(as shown in the Zeroer definition below).

Finally, look at the constructor. Every time you create a Zeroer object, its constructor automatically clears the buffer. Here's the complete definition of Zeroer:

template <int N> class Zeroer
private: //disabled operations 
 void * operator new(unsigned int)=delete;
 Zeroer(const Zeroer&)=delete;
 Zeroer& operator=(const Zeroer&)=delete;
 Zeroer(){ memset(this, 0, N); } //clear the buffer 
 void* operator new(unsigned int n, char* p) {return p; }

Here is Zeroer in action:

int main()
 char buff[512];
  new (buff) Zeroer<512>; //Zeroer ctor clears buff
  strcpy(buff,"hello"); //fill the buffer with data
  new (buff) Zeroer<512>; //clear the buffer again
  strcpy(buff,"world"); //fill the buffer again

Zeroer objects are never destroyed because they have nothing to destroy. They don't own the buffer; they merely "iron" it after every use. Every time you want to clear buff, you construct a new instance of Zeroer<512> on top of buff using placement new. In real world code, constants and typedefs will eliminate the use of hard-coded numbers:

const int PAGE_SIZE=512;
typedef Zeroer<PAGE_SIZE> ZP;

int main()
 Zeroer<52> z; //error, destructor is inaccessible
 new Zeroer<1024> ; // error, new is inaccessible

   //handle two different buffers at once
 char buff[PAGE_SIZE]; 
 char sms_buff[161];
  //process and recycle
 new (sms_buff) Zeroer<161>; //clear sms_buffer
 new (buff) ZP; //clear buff
 new (buff) ZP; //clear buff again
 strcpy(sms_buffer,"call me");  
 new (sms_buffer) Zeroer<161>; //clear sms_buffer again

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date