Discussion
Team LiB
Previous Section Next Section

Discussion

You rarely need to provide a custom new or delete, but if you need them you usually don't want to hide the built-in signatures.

In C++, after you define a name in a scope (e.g., in a class scope), it will hide the same name in all enclosing scopes (e.g., in base classes or enclosing namespaces), and overloading never happens across scopes. And when said name is operator new, you need to be extra cautious lest you make life hard for your class's clients.

Say you define a class-specific operator new:



class C {


 // …


 static void* operator new(size_t, MemoryPool&);// hides three normal forms


};



Then, if someone tries to write an expression as boring as plain old new C, the compiler will reject it on grounds that it can't find plain old operator new. Declaring the C::operator new overload that takes a MemoryPool hides all other overloads, including the familiar built-in global versions that we all know and love, to wit:



void* operator new(std::size_t);                               // plain new


void* operator new(std::size_t, std::nothrow_t) throw();       // nothrow new


void* operator new(std::size_t, void*);                        // in-place new



Alternatively, perhaps your class provides a class-specific version of one of these three operators new. In that case, by declaring one of them your class will by default also mask the other two:



class C {


 // …


 static void* operator new(size_t, void*); // hides other two normal forms


};



Prefer to have class C explicitly bring into scope all three of the standard variants of operator new. Normally, all should have the same visibility. (The visibility can still be made private for individual forms, such as if you want to explicitly disable the plain or non-throwing operator new, but the purpose of this Item is to remind you not to hide them inadvertently.)

Note that you should always avoid hiding in-place new because STL containers use it extensively.

The only remaining trick is that exposing the hidden operators new needs to be done in two different ways in two different circumstances. If your class's base class also defines operator new, all you need to do to "unhide" operator new is:



class C : public B {// …


public:


 using B::operator new;


};



Otherwise, if there is no base class version or the base class doesn't define operator new, you will need to write some short forwarding functions (because you can't employ using to pull names from the global namespace):



class C {// …


public:


 static void* operator new(std::size_t s) {


  return ::operator new(s);


 }





 static void* operator new(std::size_t s, std::nothrow_t nt) throw() {


  return ::operator new(s, nt);


 }


 static void* operator new(std::size_t s, void* p) {


  return ::operator new(s, p);


 }


};



The advice above also applies to the array forms of operator new[] and operator delete[].

Avoid calling the new (nothrow) version in client code, but provide it to save some clients from surprises if they happen to use it.

    Team LiB
    Previous Section Next Section