bhavesh
bhavesh

Reputation: 1406

Understanding Copy Constructor

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?

Upvotes: 0

Views: 234

Answers (4)

Thomas Matthews
Thomas Matthews

Reputation: 57749

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?

Upvotes: 1

slowstart
slowstart

Reputation: 333

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]

Upvotes: 0

Logan Murphy
Logan Murphy

Reputation: 6230

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.

Upvotes: 0

James Kanze
James Kanze

Reputation: 154047

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.)

Upvotes: 5

Related Questions