2

Assume a pointer object is being allocated on one point and it is being returned to different nested functions. At one point, I want to de-allocate this pointer after checking whether it is valid or already de-allocated by someone.

Is there any guarantee that any of these will work?

if(ptr != NULL)
   delete ptr;

OR

if(ptr)
   delete ptr;

This code does not work. It always gives Segmentation Fault

#include <iostream>
class A
{
    public:
    int x;
     A(int a){ x=a;}
     ~A()
     { 
          if(this || this != NULL) 
              delete this;
     }
};
int main()
{ 
    A *a = new A(3);
    delete a;
    a=NULL;
}

EDIT

Whenever we talk about pointers, people start asking, why not use Smart Pointers. Just because smart pointers are there, everyone cannot use it.

We may be working on systems which use old style pointers. We cannot convert all of them to smart pointers, one fine day.

cppcoder
  • 22,227
  • 6
  • 56
  • 81
  • @cppcoder: Do you know about `shared_ptr`? Why are you not using it? – tenfour Jun 22 '12 at 09:50
  • 2
    Mandatory read: [Why doesn't delete set the pointer to NULL ?](http://stackoverflow.com/a/704495/147192) => because it's useless. – Matthieu M. Jun 22 '12 at 09:54
  • @tenfour - I know. But now I am asking about normal pointers. – cppcoder Jun 22 '12 at 09:56
  • @cppcoder: you are asking about normal pointers, but this may be because you are asking the wrong questions! the c++11 smart pointers take care of almost all memory management requirements, and I would be rather startled if you were doing something so arcane that they couldn't work, and yet still be uncertain of the workings of `delete`. – Rook Jun 22 '12 at 11:10
  • 1
    +1 for the last edit. But this is vote-magnet 101 - use smart pointers, don't use arrays, use the std, use boost... – Luchian Grigore Jun 22 '12 at 12:15
  • 3
    The answer is *simple* and yet *hard*: **design**. Resource management is part of the design, and a important part. Rework your design until you have a clear understanding of who is/are the owners of the resources, and *then* let the owner handle it. Once the owner has been identified and only he deletes, there will be no need to *guess* whether someone else might have deleted the object... – David Rodríguez - dribeas Jun 22 '12 at 12:25
  • I would just like to suggest that switching to auto pointers or smart pointers without already having a firm grasp on basic pointers is a recipe for really bad code. auto pointers & smart pointers don't eliminate the need for a good design in the first place, they just make it easier to code up that design without missing something, esp. in the case of exceptions. But they don't magically handle object ownership issues, and if you think they do, that explains why your applications keep crashing due to double/triple/quadruple deletes. – phonetagger Jun 24 '12 at 19:44
  • +1 for David Rodríguez's comment above. That is the core of this whole issue. In the absence of good resource management design, all of the other discussion on this page is meaningless. You can make the same mistakes with auto pointers that you make with standard pointers, just in slightly different ways. – phonetagger Jun 24 '12 at 19:47

5 Answers5

6

if(ptr != NULL) delete ptr;

OR

if(ptr) delete ptr;

The two are actually equivalent, and also the same as delete ptr;, because calling delete on a NULL pointer is guaranteed to work (as in, it does nothing).

And they are not guaranteed to work if ptr is a dangling pointer.

Meaning:

int* x = new int;
int* ptr = x;
//ptr and x point to the same location
delete x;
//x is deleted, but ptr still points to the same location
x = NULL;
//even if x is set to NULL, ptr is not changed
if (ptr)  //this is true
   delete ptr;   //this invokes undefined behavior

In your specific code, you get the exception because you call delete this in the destructor, which in turn calls the destructor again. Since this is never NULL, you'll get a STACK OVERFLOW because the destructor will go uncontrollably recursive.

phonetagger
  • 7,701
  • 3
  • 31
  • 55
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • @cppcoder yes, you can't assume a pointer is valid just because it's not NULL. – Luchian Grigore Jun 22 '12 at 09:50
  • So, on what basis, I should delete my pointer. Please tell me the case of normal pointers and not smart pointers. – cppcoder Jun 22 '12 at 09:54
  • 2
    You do not need the test - `delete` a pointer that is null works fine. See http://stackoverflow.com/questions/4190703/is-it-safe-to-delete-a-null-pointer – Ed Heal Jun 22 '12 at 09:56
  • 1
    @cppcoder: When function X deletes a pointer, function Y has absolutely no way of knowing that. You are essentially trying to rewrite `shared_ptr`, so if you want to know how it works, you can always look at the source code. – tenfour Jun 22 '12 at 09:58
  • 1
    _"is not guaranteed to work [...] In fact, it's undefined behavior if ptr is a dangling pointer."_ You probably meant the right thing (it _does not prevent_ UB if ptr a dangling pointer), but I think it's worded a bit confusingly. It is in fact _guaranteed_ to work, but none better than it's already guaranteed to work anyway (`delete` on a null pointer is specified as no-op). Insofar, checking for a null pointer doesn't add anything. – Damon Jun 22 '12 at 10:06
  • Hmm, "no-op" is a bit exaggerated. It does do something, namely checking against 0, therefore, it does not do nothing, hence, it can't be a no-op. – Sebastian Mach Jun 22 '12 at 10:08
  • @Damon - The test does add something. Some CPU time! – Ed Heal Jun 22 '12 at 10:08
  • @EdHeal I believe it will be optimized out by the compiler. – Luchian Grigore Jun 22 '12 at 10:32
  • @cppcoder there's no way to guard against a double delete with raw pointers. – Luchian Grigore Jun 22 '12 at 10:33
  • @LuchianGrigore - Depends on the compile, level of optimization one supposes – Ed Heal Jun 22 '12 at 10:40
  • Hmm, the check against NULL/0/nullptr before using `delete` is redundant; `delete` will do this check already. – Sebastian Mach May 17 '13 at 15:16
4

Do not call delete this in the destructor:

5.3.5, Delete: If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted.

Therefore, you will have infinite recursion inside the destructor.

Then:

if (p)
    delete p;

the check for p being not null (if (x) in C++ means if x != 0) is superfluous. delete does that check already.

This would be valid:

class Foo {
public:
    Foo () : p(0) {}
    ~Foo() { delete p; }
private:
    int *p;

    // Handcrafting copy assignment for classes that store 
    // pointers is seriously non-trivial, so forbid copying:
    Foo (Foo const&) = delete;
    Foo& operator= (Foo const &) = delete;
};

Do not assume any builtin type, like int, float or pointer to something, to be initialized automatically, therefore, do not assume them to be 0 when not explicitly initializing them (only global variables will be zero-initialized):

8.5 Initializers: If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value. [ Note: Objects with static or thread storage duration are zero-initialized

So: Always initialize builtin types!


My question is how should I avoid double delete of a pointer and prevent crash.

Destructors are ought to be entered and left exactly once. Not zero times, not two times, once.

And if you have multiple places that can reach the pointer, but are unsure about when you are allowed to delete, i.e. if you find yourself bookkeeping, use a more trivial algorithm, more trivial rules, or smart-pointers, like std::shared_ptr or std::unique_ptr:

class Foo {
public:
    Foo (std::shared_ptr<int> tehInt) : tehInt_(tehInt) {}
private:
    std::shared_ptr<int> tehInt_;
};

int main() {
    std::shared_ptr<int> tehInt;
    Foo foo (tehInt);
}
Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
  • My question is how should I avoid double delete of a pointer and prevent crash. – cppcoder Jun 22 '12 at 10:08
  • You don't have to avoid double-delete and destructors. Destructors are ought to be called exactly once. – Sebastian Mach Jun 22 '12 at 10:15
  • So what happens if I do an accidental double delete in my code – cppcoder Jun 22 '12 at 10:22
  • 3
    @cppcoder - It's your responsility to not do that, just like you shouldn't divide by zero, subtract when you want to add two numbers, or format the hard disk. Avoid using raw pointers is a first step! – Bo Persson Jun 22 '12 at 10:26
  • @cppcoder - If you're having problems knowing whether the pointer is valid and who (what object or what function) should delete it, then you need to step back to the drawing board and review your design. Your design should specify who owns each object (i.e. allocation) and who is responsible for managing its lifetime. In a more OO-centric design (even if not completely OO), the object's manager is perhaps the gateway others should be going through to even access the object you're talking about, so each time someone needs it, they ask the owner for a reference/pointer to it. – phonetagger Jun 22 '12 at 15:49
  • ...and if the object doesn't exist at that point, depending on your design, the owner/manager can return NULL, or create a new object on the fly & return a reference/pointer to the newly created object. – phonetagger Jun 22 '12 at 15:50
  • @phonetagger - and if the pointer gets passed on to nested functions n the caller side? – cppcoder Jun 22 '12 at 16:02
  • @cppcoder – What would delete the object? The object will continue to exist until somebody deletes it. In a single-threaded app, that's not going to happen unless the caller or its callees does so. If the thing from which you got the pointer continues to own the object, then this “caller” shouldn’t delete it, nor should any nested functions that it calls (unless they’re calling the object’s owner in a way that prompts it to delete the object). Without some concrete examples of what you’re trying to accomplish, it’s difficult to continue talking about this in vague generalities. – phonetagger Jun 24 '12 at 19:02
  • From looking at some of the other questions you've asked, it seems like you really don't have a 'handle' (no pun intended) on pointers. A pointer just holds an address... just a number... and that by itself says nothing about whether the object that (may have) existed at that address still exists. You can make & have as many copies of that pointer as you want, but by themselves none of those tell you whether there is still an allocated object at that address, or if there is, if it's the object that was there when the pointer was first initialized. – phonetagger Jun 24 '12 at 19:15
  • That's why clear agreements about object ownership are so important. You can't have two places in the code both thinking they own a single object and then both of them deleting it, and if nobody thinks they own an object, that's where you'll get memory leaks and eventually run out of memory in a long-running program. You need to figure that out in your design. It's also OK to pass ownership around, as long as all parties agree on the protocol for doing so. Once you have a clean design, you won't have any of this ownership & object lifetime confusion, even with plain vanilla pointers. – phonetagger Jun 24 '12 at 19:20
  • It's also not clear to me if you understand what it means to be single-threaded. If you're programming in C++ with a regular C++ compiler, and you're not explicitly using some sort of OS/runtime facility for creating multiple threads, then your app is single-threaded. If it's single-threaded, then only one path of code execution exists, so objects won't mysteriously disappear without this "caller's" awareness, so the pointer it gets from the object's manager will continue to be valid unless this caller itself deletes the object or one of its own callees does so. – phonetagger Jun 24 '12 at 19:25
  • And if your design permits the object to disappear in the middle of program execution, then any users of the object should be required to get a new pointer from the manager any time they need to interact with the object; they should not be remembering the pointer value from some long-ago call and continuing to use it... unless your design specifies that such an object will continue to exist (never be deleted) for the duration of the program. – phonetagger Jun 24 '12 at 19:28
2

You cannot assume that the pointer will be set to NULL after someone has deleted it. This is certainly the case with embarcadero C++ Builder XE. It may get set to NULL afterwards but do not use the fact that it is not to allow your code to delete it again.

mathematician1975
  • 21,161
  • 6
  • 59
  • 101
0

You ask: "At one point, I want to de-allocate this pointer after checking whether it is valid or already de-allocated by someone."

There is no portable way in C/C++ to check if a >naked pointer< is valid or not. That's it. End of story right there. You can't do it. Again: only if you use a naked, or C-style pointer. There are other kinds of pointers that don't have that issue, so why don't use them instead!

Now the question becomes: why the heck do you insist that you should use naked pointers? Don't use naked pointers, use std::shared_ptr and std::weak_ptr appropriately, and you won't even need to worry about deleting anything. It'll get deleted automatically when the last pointer goes out of scope. Below is an example.

The example code shows that there are two object instances allocated on the heap: an integer, and a Holder. As test() returns, the returned std::auto_ptr<Holder> is not used by the caller main(). Thus the pointer gets destructed, thus deleting the instance of the Holder class. As the instance is destructed, it destructs the pointer to the instance of the integer -- the second of the two pointers that point at that integer. Then myInt gets destructed as well, and thus the last pointer alive to the integer is destroyed, and the memory is freed. Automagically and without worries.

class Holder {
  std::auto_ptr<int> data;
public:
  Holder(const std::auto_ptr<int> & d) : data(d) {}
}

std::auto_ptr<Holder> test() {
  std::auto_ptr<int> myInt = new int;
  std::auto_ptr<Holder> myHolder = new Holder(myInt);
  return myHolder;
}

int main(int, char**) {
  test(); // notice we don't do any deallocations!
}

Simply don't use naked pointers in C++, there's no good reason for it. It only lets you shoot yourself in the foot. Multiple times. With abandon ;)

The rough guidelines for smart pointers go as follows:

  • std::auto_ptr -- use when the scope is the sole owner of an object, and the lifetime of the object ends when the scope dies. Thus, if auto_ptr is a class member, it must make sense that the pointed-to object gets deletes when the instance of the class gets destroyed. Same goes for using it as an automatic variable in a function. In all other cases, don't use it.

  • std::shared_ptr -- its use implies ownership, potentially shared among multiple pointers. The pointed-to object's lifetime ends when the last pointer to it gets destroyed. Makes managing lifetime of objects quite trivial, but beware of circular references. If Class1 owns an instance of Class2, and that very same instance of Class2 owns the former instance of Class1, then the pointers themselves won't ever delete the classes.

  • std::weak_ptr -- its use implies non-ownership. It cannot be used directly, but has to be converted back to a shared_ptr before use. A weak_ptr will not prevent an object from being destroyed, so it doesn't present circular reference issues. It is otherwise safe in that you can't use it if it's dangling. It will assert or present you with a null pointer, causing an immediate segfault. Using dangling pointers is way worse, because they often appear to work.

    That's in fact the main benefit of weak_ptr: with a naked C-style pointer, you'll never know if someone has deleted the object or not. A weak_ptr knows when the last shared_ptr went out of scope, and it will prevent you from using the object. You can even ask it whether it's still valid: the expired() method returns true if the object was deleted.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
-1

You should never use delete this. For two reasons, the destructor is in the process of deleting the memory and is giving you the opportunity to tidy up (release OS resources, do a delete any pointers in the object that the object has created). Secondly, the object may be on the stack.

Ed Heal
  • 59,252
  • 17
  • 87
  • 127
  • There's nothing wrong with `delete this` *if used appropriately*. If you know an object is not going to be allocated on the stack, and you're not calling it from the object's destructor, the "suicide pattern" does have some small, specialised uses. – Rook Jun 22 '12 at 11:04
  • @Rook - Granted (small & specialized being the operative word). IMHO best to be avoided. – Ed Heal Jun 22 '12 at 11:07
  • Fair. Certainly it is never the correct thing to do if you are uncertain about the use of `delete`! – Rook Jun 22 '12 at 11:13
  • `delete this` is a very common for COM objects. – tenfour Jun 22 '12 at 12:25