tags:

views:

128

answers:

6

Hi,

I am developing a tool like memory leak detector. I can track the placement new but how can i track the placement delete. I did a lot of R & D and i found that placement delete cant be called directly, it is called by constructor at the time of exception. So how can i keep the track of placement delete against placement new?

Any help would be appreciated......

+2  A: 

You don't, there's nothing to keep track of.

Placement new means putting an object at a memory location. The storage for it has already been allocated. You don't call any form of delete on the object itself because that's the job of where you got the storage from.

That is, in this:

void* memory = operator new(sizeof(int)); // A
new (memory) int; // B
operator delete(memory); // C

A and C are what you should be tracking, not B.

GMan
Its fine when we use placement new for int, float, string but when i use placement new for my class and in the constructor of that class, there is one more new statement occur like:class MyClass{ private: char * a; public: MyClass(char * c) { a =new char[10]; strcpy(a, c); } ~MyClass() { delete[] a; }};then if we direct delete the memory(in your example) then there will be a memory leak of 10 bytes. To prevent it we have to call the destructor.
max_dev
That's only what a placement new using the standard library reserved placement form of operator new does. In general, a placement new expression is just a mechanism to pass more parameters to an allocation function. Most placement new expressions will cause memory to be allocated.
Charles Bailey
@max: Right, and that should be wrapped up. If you're placement newing, it's your job to make sure things are done right. In good code, that'll be wrapped into a resource so cleanup happens automatically.
GMan
@Charles: The standard's a bit vague, as the std::nothrow versions, for example, are called "placement version of a new expression" but *aren't* in 18.4.1.3 Placement forms, which does say "placement new" as if it is a single entity.
Roger Pate
@Roger Pate: I'm not sure I really agree that there's much vagueness on this point. There are frequent references to _a_ placement allocation function and _a_ placement deallocation function other than the library ones taking `void*`. The optional bit in the _new-expression_ grammar is called _new-placement_ and there are further references to "placement version of a _new-expression_" other than in the context of using the standard placement forms from 18.4.1.3. I don't think that referring to a "placement version of a _new-expression_" as a "placement new expression" is too much of a leap.
Charles Bailey
@Charles: It's not too much of a leap, but this specific new overload is the only form the standard puts in the "placement forms" section and the one most talked about: it's not unreasonable to believe someone means this form when they say "placement new", unless they or their code clarifies. Inconsistent might get my point across better than vague.
Roger Pate
A: 

1) use placement new for new allocs

2) use a wrapper function (such as) delete_object which calls through your allocator, and one for delete_array. note that the pointer's alignment may be offset from the actual allocation you returned (for arrays).

Justin
A: 

declare operator new/new[]/delete/delete[], (as well as the builtin variants, or hide them) in the interface of a base class. then implement the ones you'll need.

this should be usable for some purposes, if all you want to do is track leaks of objects within a specific class hierarchy (or set of).

illustration (beware, pseudo code follows):

/* option 1) use a specified allocator for all class instances */
namespace { const char* const TypeID("MON::t_object"); }

void* MON::t_object::operator new(size_t size) {
    void* const allocation(AllocatorForType(TypeID).operator_new(size));
    std::cout << "MON::t_object::operator new - size: " << size << " TypeID: " << TypeID << " address: " << allocation << "\n";
    return allocation;
}

/* option 2) use a specific allocator via placement, or type overload */
void* MON::t_object::operator new(size_t size, t_allocator& allocator) {
    void* const allocation(allocator.operator_new(size));
    std::cout << "MON::t_object::operator new - size: " << size << " allocator: " << & allocator << " address: " << allocation << "\n";
    return allocation;
}

void MON::t_object::operator delete(void* allocation) {

    std::cout << "MON::t_object::operator delete: " << allocation << "\n";

    /* now call through your allocator interface */
    if ( /* using option 1 */ ) {
        AllocatorForType(TypeID).operator_delete(allocation);
    }
    else /* using option 2 */ {
        AllocatorForAllocationWithAddress(allocation).operator_delete(allocation);
    }
}
Justin
i have already done for new/new[]/delete/delete[] and placement new and i am able to keep track of those, but noy able to keep track of placement delete
max_dev
yes, as you mentioned in your OP, placement delete will be called only when there is an exception in `new` - not when `delete` is called. response updated with example.
Justin
note that the example only covers singular allocations - you'll also need implementations for operators `new[]` and `delete[]`
Justin
+5  A: 

You want to pair allocation and deallocation:

  • malloc / free
  • new / delete (the "regular" forms)
  • new[] / delete[]

But what do you pair with placement new? (Explicitly: the one that takes a void* and commonly called simply "placement new", instead of other placement forms of new.) It's not delete, but an explicit destructor call.

  • T *p = new(mem) T(); / p->~T()

Placement new doesn't actually allocate anything, it's just syntactic sugar for calling a constructor. You don't need to, and shouldn't, track it. It's even a bit weirder than other forms, as it's not unusual to call the "destroy" bit first, then replace the destroyed object with another (the opposite of the sequence for others):

{
  T some_object;

  some_object->~T(); // die! die! die!
  new(&some_object) T();  // didn't save the return value? memory leak..? nope.
} // some_object leaves scope and is destructed (again)
Roger Pate
@Roger: Nicely explained and completely agree. This reminds me when using placement new is about the only scenario we would call an destructor explicitly.
Als
+1 totally agree, placement new doesn't actually allocate any memory so there is nothing to delete (or keep track of). I believe OPs problem lies in incorrect memory management within constructors.
Chris Bednarski
A: 

First, not to the OP but to other readers: as I understand it the OP is not talking about constructing an object in preallocated storage.

And I'm not talking about that.

To OP: you don't, just let your placement form of operator delete forward to the ordinary operator delete. After all that's the deallocation function that will be called for any successfully constructed dynamically allocated object. So you need to support it no matter what.

If you want to associate debug information with the allocated memory for use in the deallocation function then one practical option is to allocate a bit more than requested and place the info at the start or end of that block, return pointer to the unused portion. Then operator delete needs to do the opposite. Caution: alignment.

I guess an impractical option is to use a static std::map (or other associative array, like a hash table). It runs in thread safety issues and such, but it avoids alignment issue.

Cheers & hth.,

Alf P. Steinbach
Someone who preferred to be anonymous voted this (correct) answer down. Please always use a comment to explain why you vote something down. It will help others.
Alf P. Steinbach
A: 

I have a comment regarding one of your examples:

MyClass(char * c)
{
  a = new char[10];
  strcpy(a, c);
  throw here ...
}
~MyClass()
{
  delete[] a;
}

I think your problem lies in using new within constructors without wrapping it in some sort of a resource manager. If the constructor throws, no matter how an object has been newed within it (new or placement new), the memory allocated to it will leak unless it is managed by another object.

struct Janitor {
    Janitor(char* item) : mgr(item) {}
    ~Janitor() { if uncaught_exception() delete [] mgr; }
    char *mgr;
};

MyClass(char * c)
{
   Janitor j(new char[10]);
        // j is destroyed both if the rest of the contstructor succeeds
        // and if it throws

   //unsafe code
   strcpy(j.mgr, c);
   ...

   //at the end
   //pass the resource management to MyClass
   a = j.mgr;
};

Some of these may be helpful as well.
http://www.gotw.ca/gotw/008.htm
http://www.gotw.ca/gotw/056.htm
http://www.gotw.ca/gotw/010.htm
http://www.gotw.ca/gotw/022.htm
http://www.gotw.ca/gotw/042.htm

Chris Bednarski