- Overview
- Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
- Tips and Techniques
- A Tour of C99
- C++0X: The New Face of Standard C++
- C++0x Concurrency
- The Reflecting Circle New
- We Have Mail
- Converting a String to Upper Case
- Calling Member Functions with an Invalid this
- Passing an Array to a Function
- Fast Object Initialization
- C++ Sudoko, or How to Become a Real Hacker
- Member Name Lookup
- An Operating Overloading Question
- Living Without New
- Solving a Mysterious Compilation Error
- A Magical printf() Call or a Buggy Mess?
- Submitting a Library to the Standards Committee
- The Portable Datatypes Dilemma
- Can I Assign Pointers to a Specific Memory Address?
- Header File Naming Issues
- The Linkage of Enumerations
- unique_ptr And The Deprecation Of auto_ptr
- A const Member Function Conundrum
- Bugzilla
- A Response to Linus Torvalds' C++ Diatribe
- Pseudo Constructor Initializers
- Comment Terminators and The Keyword int
- Questions About enum Types
- Pointers to volatile
- Warnings on const Declarations
- Questions About inline
- The Costs of Virtual Functions
- More on Rvalue References New
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
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);


Account Sign In
View your cart