Preventing dynamic allocation

March 09, 2010

Dan_Saks-March 09, 2010

Last summer, I wrote a column entitled "Poor reasons for rejecting C++" in which I sought to dispel some misconceptions about C++.1 Among the many reader comments posted online were some valid concerns that merit further discussion. I'll address one of those concerns this month.

Some readers took exception to my statement that, "I know of no place where the C++ language performs dynamic allocation or recursion behind the scenes." As I explained a few months ago, I think of the C and C++ programming languages as distinct from their accompanying standard libraries.2 Although the various components in the Standard C++ library perform dynamic allocation behind the scenes, the language itself does not.

Nonetheless, you may be legitimately concerned that your C++ code will invoke a function that uses dynamic allocation against your wishes. In that case, you can choose among various techniques that will trigger a compile or link error to alert you that your code is using dynamic allocation.

Probably the simplest such technique is to replace the global operator new with a version that causes a link error. As I explained in an earlier column, a C++ new-expression allocates memory by calling a function named operator new.3 Each C++ environment provides a default implementation for a global operator new, declared as:

void *operator new(std::size_t n) throw (std::bad_alloc);

However, the C++ Standard lets you define a function with the same name and signature (parameter types) to replace the default implementation.4

To prevent using dynamic allocation, simply define a replacement version of operator new as follows:

// declare this function, but don't define it
void *operator_new_blocker();

void *operator new(std::size_t) throw (std::bad_alloc)
    return operator_new_blocker();

Hence, a new-expression as in:

int *p = new int;

will compile, as always, to call operator new. When the linker drags the definition for operator new into the executable, it will also try to drag in operator_new_blocker as well. However, the link will fail because operator_new_blocker isn't defined. Any call to operator new will provoke a link error, whether the call occurs directly in your code or indirectly in library code that your code uses.

Compile errors are generally preferable to link errors because compile errors tend to be more accurate in pinpointing the location and describing the nature of translation errors. However, you can't use compile-time techniques to intercept calls to operator new in previously-compiled components that you're linking into your code. You have to rely on the linker.

You must place the replacement definition for operator new so that the linker will bind the definition into the executable program if and only if at least one call to operator new occurs somewhere in the program. That is, you must ensure that the linker won't incorporate the replacement definition unless the program actually calls operator new. If you inadvertently link operator new into your program, the linker will hunt for a definition for operator_new_blocker that it won't find, and you'll get spurious link errors.

Where you need to place your definition for operator new depends on your development tools. Many modern compilers and linkers employ some form of smart linking that will link into the executable only those external functions and data that the program actually uses. For example, if a single source file contains external definitions for functions f, g, and h, yet the program calls only f and h, a smart linker will link f and h into the program, but omit g. If you're using a compiler with a smart linker, you can place the definition for operator new in any source module that's compiled and linked as part of the build. A program that never calls operator new should still link without complaint.

< Previous
Page 1 of 2
Next >

Loading comments...