0

I have read that if we have pointers inside our class then we need to implement our own copy constructor; otherwise, two classes will have pointers pointing to the same memory location, and calling delete on one of them makes the other null as well. I am trying to simulate the above condition by writing a code like this:

class A
{
    private:
        int *p;
    public:
        A()
        {
            p = new int(10);
        }

        ~A()
        {
            delete p;
            cout << "Calling destructor" << endl;
        }
};

int main(int argc, char **argv)
{
    A a;
    A aa = a;
}

I am expecting some exception to be thrown as I have not explicitly declared my copy constructor and I am using pointers as well. But program runs perfectly. Can anybody please suggest a modification so that I am able to understand under what condition exception will occur?

bhavesh
  • 1,137
  • 2
  • 11
  • 22
  • You forgot the assignment operator and the move versions. This is why a smart pointer is a better idea. Also, `delete` doesn't null the pointer. – chris Aug 06 '13 at 18:44
  • You only need a copy cpnstructor if you have allocagted objects, which shouldn't be passed on. And the pointer in the other object doesn't become NULL it becomes invalid if you delete the first copy. Sharing pointers is perfectly well allowed. It depends on your class if this is ok or not. – Devolus Aug 06 '13 at 18:45
  • @chris The move versions aren't necessary. They're only an optimization that you can do when the profiler says you need it. – James Kanze Aug 06 '13 at 18:49
  • The default copy ctor and assignment operators perform a member-wise copy. See also http://stackoverflow.com/questions/9169774/what-happens-in-a-double-delete – sbaker Aug 06 '13 at 18:50
  • @JamesKanze, Fair enough, that applies more for arrays than just single elements (and a built-in type at that) when it comes to pointers. – chris Aug 06 '13 at 18:52

4 Answers4

5

Your code does delete the same pointer twice. This is undefined behavior, and one of the possible symptoms of undefined behavior is that it can seem to work. (In my experience, the most frequent symptom is that everything works perfectly until you present it to the public, at which time, it starts crashing right and left.)

James Kanze
  • 142,482
  • 15
  • 169
  • 310
  • that's what I wanted to see. Can you please suggest an example where compiler throws an exception on countering NULL pointer deletion. – bhavesh Aug 06 '13 at 18:50
  • @bhavesh, The standard says that deleting a null pointer is a noop, so any compiler that does something else is non-conforming. The pointer isn't being nulled in the first place, though. – chris Aug 06 '13 at 18:54
  • @bhavesh It's perfectly legal to delete a null pointer. There are no null pointers in the example, however, only invalid pointers; the first call of the destructor of `A` invalidates the pointer in the second instance of the `A`, and _any_ use of that pointer (including just copying it) is undefined behavior. Which means that anything can happen; there are debugging implementations which will cause the program to crash on the second delete, or to output a screenful of diagnostic data. – James Kanze Aug 07 '13 at 08:09
1

Your issue is the same as having two pointers pointing to one instance and the instance is deleted.

Given the following code:

int main(void)
{
    int * pointer_1 = NULL;
    int * pointer_2 = NULL;

    pointer_1 = new int;
    *pointer_1 = 42;

    // Make both pointers point to the same dynamically allocated object.
    pointer_2 = pointer_1;

    // Let's delete the instance
    delete pointer_1;

    // The delete operator does not change the value of pointer_1.
    // Pointer_1 still points to *something*, but that *something* has been deleted.

    return 0;
}

In the above example, delete does not affect the value of pointer_1 or pointer_2. Only the object isn't there anymore. Nothing in the C or C++ standard says that the program must be notified when the memory is deleted or the pointers changed.

There is nothing stating that when memory is deleted, the implementation must change every pointer that was pointing to the memory deleted.

Because the object isn't there anymore, deferencing the pointers will generate undefined behavior. The contents may have a shadow in memory, or the operating system may have removed the page completely from memory (a.k.a. memory paging).

The OS may throw an exception but the C and C++ languages do not force compiler libraries to generate exceptions. After all, in some embedded systems, address 0 is a valid memory location.

Try printing the value of the pointers at each step to verify.

I tell you and your friend to point to a rug on the floor. I remove the rug. What are you and your friend pointing to?

Thomas Matthews
  • 52,985
  • 12
  • 85
  • 144
0

Something like this

A * a = new A();
A * b = a;
delete a;
stdout << (*(b->p));

But delete should only make the memory free for other parts of your program to use, not necessarily null it.

Logan Murphy
  • 5,700
  • 2
  • 21
  • 39
  • even if my class had a simple int x = 10 then also b->x would give garbage value. I just wanted to have an example where default copy constructor fails in case of copying pointers. – bhavesh Aug 06 '13 at 19:01
  • @bhavesh, Your example does fail. It's undefined behaviour. Rather than be notified of this, why not choose a robust solution like a smart pointer that just works in the first place? – chris Aug 06 '13 at 19:04
  • I do not know what you mean by the default constructor, when you set aa = a you are just changing memory, not invoking any kind of constructor – Logan Murphy Aug 06 '13 at 19:08
  • @LoganMurphy: When `=` appears in an initialization (as in the OP's code) it calls the copy constructor. – Benjamin Lindley Aug 06 '13 at 19:11
  • @Benjamin Lindley Oh because it is not stored by reference? I guess you learn something new everyday ty :) – Logan Murphy Aug 06 '13 at 19:12
0

I don't think this will ever throw an "exception" per say. You may get undesirable side effects like SEGV or worse memory corruption. To understand why you need the copy constructor you can think of how the objects 'a' and 'aa' will look.

Assume, new int(10) returns a pointer value 0xfeedface, where *(int *)(0xfeedface) == 10. Your objects will look like,

a -> { p=0xfeedface} aa -> { p=0xfeedface}

Now if you destruct object-a the memory '0xfeedfac'e will get unallocated & go back to your allocators freelist. Consider what happens to object-aa. It is still holding a reference to 0xfeedface, but that memory that has already been freed! Now if object-aa tries to dereference *p it can get potentially random value (depending on what the allocator does or if the object has been allocated to some other object). Also, terrible things can happen if object-aa ever tries to write to p=0xfeedface.

If you want to mandate writing a copy constructor, one way i can think of is to create a base class & assert if it ever gets called.

#include <iostream>
#include <cassert>

class base
{
    public:
        virtual void operator=(const base& )
        {   
            assert(! "No copy constructor for class derived from base");
        }   
};

class derived : public base
{};

int
main()
{
   derived d, d1; 
   d1 = d;
}

The above code will assert since class derived hasn't provided with a copy constructor. It would be ideal to have this be caught at compile time instead. But I can't think of a way of doing that right now.

[caveat: the above solution won't work well for multiple levels of inheritance]

slowstart
  • 245
  • 4
  • 8