Home > Guides > Programming > C/C++

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Living Without New

Last updated Dec 2, 2005.

C++ programmers don’t always have the luxury of using a fully ISO-compliant implementation. In some cases, e.g., embedded systems and mobile devices, they have to make do with a limited subset of C++. How limited? Normally, exception handling, RTTI and various parts of the Standard Library aren’t supported. In extreme cases, critical core language features aren’t supported.

This week, I tell the story one such challenging environment – an implementation that doesn’t support new!

Writes one reader:

We have a function in a class which takes a pointer to a template class as an argument. Now, new is not supported in the OS and so for any dynamic object creation we have to resort to malloc and then call its constructor. So, in this particular function, how can we call the constructor of a template class in an elegant fashion?

Problem Analysis

The lack of new seems to be an impasse. Is there any hope for solving this problem? With a few tricks and workarounds, yes there is.

When we face such a problem, our first task is to identify its impact. What does the lack of new actually mean? How does it affect your code?

new combines two operations:

  • Allocating raw memory from the free-store. The allocated memory must be properly aligned for every object type. In this regard, new is similar to malloc.
  • Initializing the allocated object.

new implicitly invokes the constructor of the object being allocated (this rule applies to class-specific overloaded versions of new, too). When the constructor has finished, new returns the fully-constructed object’s address. This is the real challenge we’re facing.

We can use malloc to implement the first operation, i.e., dynamic allocation of properly aligned raw memory. Yet we need to ensure that the initialization of the object being allocated. Unlike other member functions, constructors, have neither a name nor an address. Consequently, you can’t call a constructor directly.

One workaround is to give up constructors altogether, and use an ordinary member function such as init(). This isn’t a satisfactory solution, because in order to call init(), you need an already constructed object!

Another approach relies on using a freestanding function that takes a raw memory buffer and initializes it. This technique may work but it’s inelegant; such a function must know all the types of object used in the program.

Let’s assume that the class template in question looks like this:

template <class T> class X 
{ 
public: 
 X<T>(): n(0) {} 
 ~X<T>() {cout<< "bye bye" << endl;} 
 void func(T t) { cout<< t <<endl; }
private:
 int n;
};

Initialization

The ideal place for the initialization operation is the class itself, because the class knows best how to initialize its instances. This is a fundamental principle of object-oriented design. We’re still stuck with the problem of calling a member function for an object that hasn’t been constructed yet. As a workaround, we use a static member function called construct(). Unlike an ordinary member function, you can call a static member function even if there are no "live" objects of the said class.

construct() takes a pointer to the raw memory buffer on which the object will be constructed. The return value is X<T>*, with T co-varying with the template’s argument:

template <class T> class X 
{ 
public: 
 static X<T>* construct(void * ptr) ;
};

construct() performs three operations:

  • Cast ptr to X<T>* .
  • Initialize the buffer to which ptr is pointing.
  • Return the address of the initialized object.

Although we can’t call a constructor directly, we can assign an already-constructed object to the raw buffer. A temporary will do the trick:

static X<T>* construct(void * ptr) 
{ 
 X<T>* pobj = (X<T>*)ptr; //step 1: cast void * to the appropriate type *pobj=X<T>(); //create a temp, assign it to the raw buffer
 return pobj;
}

construct() creates a temporary X<T> object and assigns it to *pobj. The temporary is destroyed immediately afterwards. Finally, construct returns pobj which is a pointer to an initialized object of type X<T>.

The following program uses this technique to allocate an object of type X<double> dynamically, without using new:

int main() 
{ 
 void * p = malloc (sizeof (X<double>)); 
 X<double> *pobj = X<double>::construct (p); 
 pobj->func(5.0); 

Discussions

Bugzilla
Posted Nov 18, 2008 01:53 AM by cupu
2 Replies
auto_ptr issues
Posted Sep 14, 2007 07:43 AM by singh_siddhu
1 Replies
i want c++ book through net
Posted Aug 23, 2007 11:13 PM by harivilu
3 Replies

Make a New Comment

You must log in in order to post a comment.

Related Resources

Danny KalevBjarne Stroustrups's Stevens Talk
By Danny KalevDecember 7, 2009 No Comments

On 2nd December Bjarne Stroustrup delivered a talk about the standardization process of C++0x at the Stevens institute. Here some of the key points from his talk.

Danny KalevMinutes from the October 2009 Meeting
By Danny KalevNovember 19, 2009 No Comments

The minutes from the Santa Cruz (October 2009) meeting are available here. Even if you're not a language layer at heart, I encourage you to read them.

Danny KalevA Reader's Opinion on Attributes
By Danny KalevOctober 20, 2009 No Comments

In August I dedicated a series to the debate about C++0x attributes. I believe that it covered the subject in a balanced and detailed way, but I keep getting complaints from C++ users who don't like attributes for various reasons. Here's a recent email I received from a Polish C++ programmer. While it  doesn't represent my opinion about attributes -- I'm rather neutral about this feature and consider it a "solution waiting for a problem" -- but it suggests that attributes are still a highly controversial issue that will haunt C++ for a long time. The email is quoted here with minor edits that and as usual, with all private details removed.

See More Blogs

Informit Network