Link to home
Start Free TrialLog in
Avatar of mrwad99
mrwad99Flag for United Kingdom of Great Britain and Northern Ireland

asked on

Assignment operator with pointers: is this correct ?

Hi,

Just  a quick question regarding everyones favourite operator, =.  I have been experimenting with this and have recently introduced pointers to my class, thus am wondering if I have wrote said operator correctly.  Here is my class:

#include <iostream>

using namespace std;

class Counter2
{
public:
      Counter2(int value, char* name);
      ~Counter2();
      Counter2& operator=(const Counter2& rhs);
      friend ostream& operator << (ostream& os, const Counter2& rhs);
private:
      int* itsVal;
      char* itsName;
};

Counter2::~Counter2()
{
      delete [] itsName;
      delete itsVal;
}

ostream& operator << (ostream& os, const Counter2& rhs){
      return os  << "Name: " << rhs.itsName << "\nValue: " << *(rhs.itsVal) << endl;
}


Counter2& Counter2::operator=(const Counter2& rhs)
{
      if (this == &rhs) {
            return *this;
      }
      *(this->itsVal) = *(rhs.itsVal);
      
      if ( (this->itsName = (char*)malloc(strlen(rhs.itsName)+1)) == NULL) {
            cout << "Memory allocation error";
            return *this;
      }
      strcpy(this->itsName, rhs.itsName);
      return *this;
}


Counter2::Counter2(int value, char* name)
{
      itsVal = new int(value);
      *itsVal = value;

      itsName = new char[strlen(name)+1];
      strcpy(itsName, name);
}

int main()
{
      Counter2 a(1,"Counter a");
      Counter2 b(10,"Counter b");
      a = b;
      cout << a << endl;
      cout << b << endl;

      return 0;
}

Is my definition of = correct given the class ?  Of particular concern is the memory allocation part - is this right ?

Cheers in advance.
Avatar of AlexFM
AlexFM

It's almost OK. You don't need a pointer in intVal, only single value. Corrected version:

#include <iostream>

using namespace std;

class Counter2
{
public:
    Counter2(int value, char* name);
    ~Counter2();
    Counter2& operator=(const Counter2& rhs);
    friend ostream& operator << (ostream& os, const Counter2& rhs);
private:
    int itsVal;
    char* itsName;
};

Counter2::~Counter2()
{
    if ( itsName )
        delete [] itsName;
}

ostream& operator << (ostream& os, const Counter2& rhs){
    return os  << "Name: " << rhs.itsName << "\nValue: " << *(rhs.itsVal) << endl;
}


Counter2& Counter2::operator=(const Counter2& rhs)
{
    if (this == &rhs) {
         return *this;
    }

    this->itsVal = rhs.itsVal;

    if ( itsName )
    {
        delete[] itsName;
        itsName = NULL;
    }
   
     if ( (this->itsName = (char*)malloc(strlen(rhs.itsName)+1)) == NULL) {
         cout << "Memory allocation error";
         return *this;
    }
    strcpy(this->itsName, rhs.itsName);
    return *this;
}


Counter2::Counter2(int value, char* name)
{
    itsVal = value;

    itsName = new char[strlen(name)+1];
    strcpy(itsName, name);
}

int main()
{
     Counter2 a(1,"Counter a");
    Counter2 b(10,"Counter b");
    a = b;
    cout << a << endl;
    cout << b << endl;

    return 0;
}



Change line also in operator <<

  return os  << "Name: " << rhs.itsName << "\nValue: " << rhs.itsVal << endl;
Avatar of mrwad99

ASKER

AlexFm,

Thanks for a quick response.

In response to your comments:

I am aware that I do not need itsVal to be a pointer; I added that it for variety in the = operator.

-> So there is definitely no need, if I put itsVal back as a pointer to int type, to say in the = operator

itsVal = new int()
*itsVal = *(rhs.itsVal)

is there ?

I ask as I saw this done in a book I am reading, and thought

"Why do that if we have already allocated the memory for itsVal in its constructor?"

Cheers.
Indeed, you want to do...

   itsVal = new int();
   *itsVal = *(rhs.itsVal); // More usually written as *itsVal = rhs->itsVal

... to get itsVal to point to allocated memory before putting a value into that memory.

You can use the following:

    itsVal = new int(rhs->itsVal);

That allocates memory for the int and assigns it the value too in one hit.
There's a glaring problem with this code, as is:
>> if ( (this->itsName = (char*)malloc(strlen(rhs.itsName)+1)) == NULL)

malloc is paired with delete ~Counter::Counter( ) { delete [] itsName; } //*** oops!!

Also, what about the current memory allocated to itsName. It's never deleted !

Counter2& Counter2::operator=(const Counter2& rhs)
{
//  small optimisation
    if (this == &rhs)
    {
         return *this;
    }

    Counter2 temp ( rhs );
    Swap ( this->itsName, temp.itsName );

    return *this;
}

where swap is implemented as a simple template function to swap two values.

It's a small price to pay, invoking the copy constructor, but at least you don't have to worry about reallocating memory.

BTW, it's exception safe as well.
lol ... There's a glaring problem with this code, as is:

Counter2& Counter2::operator=(const Counter2& rhs)
{
//  small optimisation
    if (this == &rhs)
    {
         return *this;
    }

    Counter2 temp ( rhs );
    Swap ( this->itsVal, temp.itsVal ); // *** oops
    Swap ( this->itsName, temp.itsName );

    return *this;
}
Avatar of mrwad99

ASKER

_ys_ ,

>> malloc is paired with delete ~Counter::Counter( ) { delete [] itsName; } //*** oops!!

I had to have a real hard look to see what the problem was with my code.  I think (now) that it is due to the fact that I need to call free(itsName) and not 'delete' to clean up the memory allocated for itsName.  Correct ?  

But then how would we go on freeing the memory that was created with 'new char[]' when an object was first created ?  Could we call free on this too ?

Also interesting point you raise about the Swap function; I would consider that if I was undertaking a huge SW project and not just experimenting with a dummy class to figure things out !


rstaveley, WHY OH WHY do we have to create a new int ??  When the object was first constructed a new integer was created, so surely it is just a case of assigning the integer pointed to the required value,

i.e. *itsVal = *(rhs.itsVal)

??

Of course please correct me if I am wrong !



Someone please clear this confusion up !
Avatar of mrwad99

ASKER

Alternatively, in the = operator, could I forget the malloc and just say:

delete [] itsName;
itsName = new char[strlen(rhs.itsName)+1];
strcpy(this->itsName, rhs.itsName);

?
ASKER CERTIFIED SOLUTION
Avatar of _ys_
_ys_

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
> rstaveley, WHY OH WHY do we have to create a new int ??  When the object was first constructed a new integer was created, so surely it is just a case of assigning the integer pointed to the required value,

Yes, you are right. I misunderstood your question.

Beware that you are missing a default constructor and default construction would leave both pointers uninitialised.

I take it you inderstood AlexFM's point that it really doesn't make sense to use an int pointer in the class to point to one int allocated outside it. If you use a plaint old int, the storage space will be automatically allocated in the class, when the class is constructed. An int pointer typically takes as much storage as an int and it requires additional storage outside the class to store the int value.

>> Beware that you are missing a default constructor and default construction would leave both pointers uninitialised.

A subtle point. The inclusion of a custom constructor forces the compiler *not* to synthesise a default one. Any code that relies on such will not compile. Such as:

Counter2 c;

But I agree with your intentions.
> The inclusion of a custom constructor forces the compiler *not* to synthesise a default one. Any code that relies on such will not compile.

Learn something new every day... I hadn't reaised that :-)
Avatar of mrwad99

ASKER

Still playing with what _ys_ said, but rstaveley:

>>Indeed, you want to do...

>>  itsVal = new int();
>> *itsVal = *(rhs.itsVal); // More usually written as *itsVal = rhs->itsVal


itsVal = rhs->itsVal will not compile since rhs is a reference not a pointer; I think.  Am I correct ?
You are right, mrwad99, I've been consistently posting bad advice in this thread. I do apologise. %-}

A trivial illustration supporting your point is as follows:
--------8<--------
#include <iostream>

struct X {int *y;};

int main()
{
    int i = 123; // Here's an int
    X x; // Here's s structure
    x.y = &i; // Stucture member points to the int
    X& x2 = x; // Let's get a reference to the structure
    std::cout << "The value is: " << *(x2.y) << '\n';
}
--------8<--------

Now I'm going to sit in the corner with my dunce hat on.
>> Still playing with what _ys_ said
Kudos to you.

Bear in mind that you will have to roll out your own copy constructor.

Counter2 (const Counter2 &rhs)
{
// allocate memory
// copy values
}

Without doing this the compiler [again] synthesises its own on your behalf, allowing your code to happily compile - this will however lead to a bitwise copy, which would be _really_ bad.
Avatar of mrwad99

ASKER

Right I think I have got it sorted now....

Excuse the swap functions (I have not learnt about templates yet :) ).

#include <iostream>

using namespace std;

void swap(int* a, int* b)
{
      int temp = *a;
      *a = *b;
      *b = temp;
}

// Not sure of this...
void swap(char* a, char* b)
{
      char* temp = a;
      a = b;
      b = temp;
}

class Counter2
{
public:
      Counter2(int value, char* name);
      ~Counter2();
      Counter2(const Counter2& rhs);
      Counter2& operator=(const Counter2& rhs);
      friend ostream& operator << (ostream& os, const Counter2& rhs);
private:
      int* itsVal;
      char* itsName;
};

Counter2::~Counter2()
{
      delete [] itsName;
      delete itsVal;
}

Counter2::Counter2(const Counter2& rhs)
{
       itsVal = new int(*(rhs.itsVal));
       itsName = new char[strlen(rhs.itsName) + 1] ;
       strcpy(itsName, rhs.itsName);
}

ostream& operator << (ostream& os, const Counter2& rhs){
      return os  << "Name: " << rhs.itsName << "\nValue: " << *(rhs.itsVal) << endl;
}


Counter2& Counter2::operator=(const Counter2& rhs)
{
      if (this == &rhs) {
            return *this;
      }
      Counter2 temp(rhs);

      swap(this->itsVal, temp.itsVal);
      swap(this->itsName, temp.itsName);
      return *this;
}


Counter2::Counter2(int value, char* name)
{
      itsVal = new int(value);
      *itsVal = value;

      itsName = new char[strlen(name)+1];
      strcpy(itsName, name);
}

int main()
{

      Counter2 a(987865,"Counter a");
      Counter2 b(10,"Counter b");
      cout << a << endl;
      cout << b << endl;
      a = b;
      cout << a << endl;
      cout << b << endl;
      return 0;
}

Correct ?
Using references provides the elegant solution you're looking for.

void swap(int* &a, int* &b)
{
     int* temp = a;
     a = b;
     b = temp;
}

void swap(char* &a, char* &b)
{
     char* temp = a;
     a = b;
     b = temp;
}
You were that close.

Revisit this when you learn about templates, and you'll be pleasantly surprised how simple your code becomes.
Just one little picky point.

Counter2::Counter2(int value, char* name)
{
     itsVal = new int(value);
//     *itsVal = value;    //*** the above line already stores this value.

     itsName = new char[strlen(name)+1];
     strcpy(itsName, name);
}
Avatar of mrwad99

ASKER

I had not even considered the fact that you could pass a *pointer* as a *reference*, but thinking about it it is just a case of

<type>& <var name>

with using references.

Also, as a closing point, is my original swap function for strings also correct as is, i.e.

// Not sure of this...
void swap(char* a, char* b)
{
    char* temp = a;
    a = b;
    b = temp;
}

?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mrwad99

ASKER

Right I have doubled the points for _ys_ since he answered more than was originally questioned, especially with the issue of 'swapping' to achieve greater efficiency.

rstaveley you deserve 20 points for showing how to swap strings; I knew this anyway but did not apply it and if you were not to correct me I would have left the code incorrect.

Thanks greatly to both of you for this ! :)
Avatar of mrwad99

ASKER

*I know I have closed this but I will award a further 75 points* if anyone can tell me why my code crashes if I rewrite swap (for char*) from

void swap(char** a, char** b)
{
   char* temp = *a;
   *a = *b;
   *b = temp;
}

to

void swap(char** a, char** b)
{
   *a = *b;
}

I am thinking why bother with the temporary variable; the value in b is only that of a temporary object anyway so it wont matter if it gets reassigned or not...will it ?

*Please try the code below for me* and explain if you can *why the program crashes at the very end*.  Also points of interest is that if I declare, in operator=,

Counter2 temp(rhs);

to be

Counter2* temp = new Counter2(rhs);

and *never call 'delete temp'*  the code DOES NOT CRASH !

Arggg ! What the heck is going on ?

I post this here and not as a new question, as you two are already familar with what is going on so it will save a lot of further explaining if you get me.

Cheers again.

FAULTY CODE:

#include <iostream>

using namespace std;

class Counter2
{
public:
      Counter2(int value, char* name);
      ~Counter2();
      Counter2(const Counter2& rhs);
      Counter2& operator=(Counter2& rhs);
      friend ostream& operator << (ostream& os, const Counter2& rhs);
private:
      int* itsVal;
      char* itsName;
};

Counter2 counterA(987865,"Counter a");
Counter2 counterB(10,"Counter b");

void mySwap(int*& a, int*& b)
{
      a = b;
}

void mySwap(char** a, char** b)
{
      *a = *b;
}


Counter2::~Counter2()
{
      if (itsName) {       delete [] itsName; }
      delete itsVal;
}

Counter2::Counter2(const Counter2& rhs)
{
      itsVal = new int(*(rhs.itsVal));
      itsName = new char[strlen(rhs.itsName) + 1] ;
      strcpy(itsName, rhs.itsName);
}

ostream& operator << (ostream& os, const Counter2& rhs){
      return os  << "Name: " << rhs.itsName << "\nValue: " << *(rhs.itsVal) << endl;
}

Counter2& Counter2::operator=(Counter2& rhs)
{
      if (this == &rhs) {
            return *this;
      }
      
      //Counter2* temp = new Counter2(rhs);

      Counter2 temp(rhs);
      mySwap(&(this->itsName), &(temp.itsName));

      cout << "'this' (counterA) after swap is\n\n" << counterA << endl;
      cout << "'rhs' (counterB) after swap is\n\n" << counterB << endl;

      cout << "deleting temp..." << endl;

      //delete temp;

      return *this;
}


Counter2::Counter2(int value, char* name)
{
      itsVal = new int(value);
      itsName = new char[strlen(name)+1];
      strcpy(itsName, name);
}

int main()
{
      counterA = counterB;
      cout << "all ok..." << endl;
      return 0;
}
>I am thinking why bother with the temporary variable; the value in b is only that of a temporary object anyway so it wont matter if it gets reassigned or not...will it ?

The destructor for the temporary object deallocates the memory assinged to itsVal. It crashes at the very end when the destructor is called for counterB attempting to deallocate the same memory.
 
Avatar of mrwad99

ASKER

>>It crashes at the very end when the destructor is called for counterB attempting to deallocate the same memory.

Right I have experimented with that and found that it cannot be the case:

I have altered the destructor to be:

Counter2::~Counter2()
{
      cout << "Destroying object " << *this << endl;
      if (itsName) {       delete [] itsName; }
      delete itsVal;
      cout << "Destruction successful" << endl;
}

and the output I get is:

'this' (counterA) after swap is

Name: Counter b
Value: 987865

'rhs' (counterB) after swap is

Name: Counter b
Value: 10

deleting temp...
Destroying object Name: Counter b
Value: 10

Destruction successful
all ok...

As we can see there is no crash *from within* the body of the destructor.  More to the point, why does the code not crash when I have the mySwap function as:


void mySwap (char** a, char** b)
{
    char* temp = *a;
    *a = *b;
    *b = temp;
}

what is it about assigning to *b (that is of the temporary object anyway) that makes all the difference ?

I have tried running it through the debugger to no avail.  

?????
Avatar of mrwad99

ASKER

OK I have made some slight changes that hopefully will make it more obvious as to what is going on:

I have moved the declarations:

      Counter2 counterA(987865,"Counter a");
      Counter2 counterB(10,"Counter b");

to be within main().

And removed the references to them from operator= as they are no longer accesible.

Now the output I get is:

deleting temp...
Destroying object Name: Counter b
Value: 10

Destruction successful
all ok...
Destroying object Name: Counter b
Value: 10

Destruction successful
Destroying object Name: &#9612;&#9612;&#9612;&#9612;&#9612;&#9612;&#9612;&#9612;&#9612;&#9612;&#9612;&#9612;&#9612;&#9612;
Value: 987865

Clearly, the name has not been copied across to this object.  But my point is WHY SHOULD THIS MATTER - I am not messing with the actual object when I fail to swap the itsName variable; only a copy of it, i.e. temp !
Put the additional debug into your destructor and you'll see your problem:

Counter2::~Counter2()
{
     cout << "Destroying object " << *this << endl;
     if (itsName) {      
          std::cout << "Freeing up the itsName memory at: 0x" << reinterpret_cast<void*>(itsName) << '\n';
          delete [] itsName;
     }
     delete itsVal;
     cout << "Destruction successful" << endl;
}

When your temporary object goes out of scope, its destructor is called (destructors are called for automatic objects when they go out of scope) and the memory allocated to itsName is released by temp's destructor. That is the same bit of free store memory, which you expect to belong to counterA.

You may find that your implementation allows you to get away with...

     int *iptr = new int();
     delete iptr;
     delete iptr; /* Oops, deleted the object twice!! */
     std::cout << "I\'m still alive!\n";

... but you are certain to get problems, if the same bit of memory is reallocated to another object before the second delete call and you are likely to get problems doing something like this:

     int *iptr = new int();
     delete iptr;
     std::cout << "Value is " << *iptr << '\n'; /* Oops, reading deallocated memory */
     *iptr = 123; /* Oops, assigned after the memory has been deallocated */

Implementations may let you get away with code like this. It depends largely on the allocator implementation. But you should certainly not expect to get away with it.
>> Right I have doubled the points for _ys_ since he answered more than was originally questioned, especially >> with the issue of 'swapping' to achieve greater efficiency.
Really not necessary, but appreciated.

I already pointed this out earlier, but feel it needs reiterated. This implementation, using Swap, is *not* more efficient - in fact it's slightly slower than your original post; due to the construction of an intermediate Counter2 instance within operator=.

The major benefit, is it's exception safety, and ease of use - the memory allocation is performed within the copy contructor.


>> void mySwap(int*& a, int*& b)
>> {
>>     a = b;
>> }

OMG, what have you done!!

And ditto to this:
>> void mySwap(char** a, char** b)
>> {
>>      *a = *b;
>> }

>>It crashes at the very end when the destructor is called for counterB attempting to deallocate the same memory.

'attempting to deallocate the same memory'
Enough said. Don't do this ... ever !!

>> Also points of interest is that if I declare, in operator=,
>> Counter2 temp(rhs);
>> to be
>> Counter2* temp = new Counter2(rhs);
>> and *never call 'delete temp'*  the code DOES NOT CRASH !
It may not crash, but it's almost sure to be a momory leak. BTW, the reason it does not crash is because it doesn't attempt to deallocate the same memory (it's never deleted - it's a memory leak !!)

>> WHY SHOULD THIS MATTER - I am not messing with the actual object when I fail to swap the itsName
>> variable; only a copy of it, i.e. temp !
An *exact* copy of it, to be precise. Let's do some code to explain.

[taking your most recent post]
Counter2& Counter2::operator=(const Counter2& rhs)
{
     Counter2 temp(rhs);
     mySwap(&(this->itsName), &(temp.itsName));

     return *this;
}

[and your mySwap(char**, char**)]
void mySwap(char** a, char** b)
{
     *a = *b;
}

Let's to inline everything and add comments. [pseudo memory addresses inserted to improve clarity]

// allocate memory for counterA->itsName (0x0001)
Counter2 counterA = new Counter2(987865,"Counter a");

// allocate memory for counterB->itsName (0x0002)
Counter2 counterB = new Counter2(10,"Counter b");

//** counterA = counterB **//
{
    // allocate memory for temp.itsName (0x0003)
    Counter temp( rhs );

    // copy memory address from temp.itsName to counterA->itsName
    // [counterA->itsName == rhs.itsName == (0x0003)]
    *(&(counterA->itsName)) = *(&(temp.itsName));

    // delete memory for temp.itsName (0x0003)
    //** temp::~Counter2 ( ); **//
}

// display contents of memory for counterA->itsName (0x0003)
cout << counterA->itsName;

// display contents of memory for counterB->itsName (0x0002)
cout << counterB->itsName;

// delete memory for counterA->itsName (0x0003)
delete counterA;

// delete memory for counterB->itsName (0x0002)
delete counterB;


You should notice that memory address 0x0003 is deleted twice !! And 0x0001 not at all !!

If you were to revert to having the extra temp variable within mySwap:
void mySwap (char** a, char** b)
{
    char* temp = *a;
    *a = *b;
    *b = temp;
}

We would have:
//** counterA = counterB **//
{
    // allocate memory for temp.itsName (0x0003)
    Counter temp( rhs );

    //** swap memory addresses temp.itsName and counterA->itsName **//
    {
        // temp == 0x0001
        char* temp = *(&(counterA->itsName));

        // counterA->itsName == 0x0003
        *(&(counterA->itsName)) = *(&(rhs.itsName));

        // rhs.itsName == 0x0001
        *(&(rhs.itsName)) = temp;
    }

    // delete memory for temp.itsName (0x0001)
    //** temp::~Counter2 ( ); **//
}

Now, the memory 0x0001 is deleted (no more memory leak !!) and 0x0003 will eventually be deleted (once and once only !!) by the statement delete counterA.
Avatar of mrwad99

ASKER

Right quick update:

Thanks both of you for that explanation; I will analyse my print-off of both answers when I get home this evening and then come back with any questions.

Cheers again !
Avatar of mrwad99

ASKER

OK this has opened up a new can of worms here regarding addresses and pointers and dereferencing them.... I thought I had this sussed from my C days but obviously not 100%....

OK then.  I have drawn lots of diagrams and realised that yes, when I do not 'repoint' (for lack of a better word) the itsName pointer from the temporary object then we have two pointers pointing to the same area of memory; deleting this pointer twice causes the crash.  Correct ?

Now then, the way I drew my diagram was to have the word 'itsName' pointing to a box containing the name, be it CounterA or CounterB.  So I had two 'itsName' words written on my paper (one for the temoporary object, and one for CounterA, as per the =operator code), each pointing to their own box containing the name.  Now, if I point itsName from CounterA at the Box that itsName from temp is pointing at, we have graphically displayed the problem: two pointers pointing to the same memory.  Again correct.  

But what I am now asking is *what happens to the box originally pointed to by itsName of CounterA, before this itsName pointed at the box of temp ?*  Is this a memory leak, that obviously would not crash my code, but would just leave memory allocated by new to be unreachable, hence, undeletable ?  

Now, second question:

I decided to visually prove my drawing through my code, so attempted to output the addresses of the variables being deleted.  I hence rewrote the destructor to be:

Counter2::~Counter2()
{
      cout << "Destroying object " << *this << endl;
      if (itsName) {       
            cout << "destroying itsName, at mem location: " << &itsName << endl;
            delete [] itsName;
      }
      delete itsVal;
      cout << "Destruction successful" << endl;
}

leaving the swap function as to cause a crash, i.e.

void mySwap(char** a, char** b)
{
    char* temp = *a;
    *a = *b;
    //*b = temp;
}

The output I got is interesting, and is shown below.

deleting temp...

Destroying object Name: Counter b Value: 10
destroying itsName, at mem location: 0012FEF4 //**********
Destruction successful

Diagnostics:

counterA: Name: <rubbish> Value: 987865
counterB: Name: Counter b Value: 10

all ok...main exiting...

Destroying object Name: Counter b Value: 10
destroying itsName, at mem location: 0012FF68
Destruction successful

Destroying object Name: <rubbish> Value: 987865
destroying itsName, at mem location: 0012FF70 //**********

<crash>

What I am interested in here is why the two lines marked (//**********) above do not show that the same area of memory is being deleted twice (which I am now certain *is* happening) ?  Or have I used the wrong notation ?

Finally, on a related note,

int main()
{
      int* a = new int(10);
      cout << a << endl;
      cout << &a << endl;
      int* b = a;
      delete a;
      cout << *a << endl;
      cout << *b << endl;
      return 0;
}

OUTPUT:
002F1210
0012FF7C
-572662307
4649844

what is the difference between a and &a ?  Clearly there is some with the output.  Also I am puzzled as to why *b does not crash the program, or is this merely because 'a' (hence b) now points a nothing ? (but still in theory, the same area of memory).  If so, why is the output for *a and *b different ?

Phew.  I think that just about exhausts everything there is to ask on this one.

Profuse thanks again !
>> deleting this pointer twice causes the crash.  Correct ?
Yes.

>> would just leave memory allocated by new to be unreachable, hence, undeletable ?
Exactly. That's why swap (when correctly implemented) does just that - it's swap the two values, allowing both of them to still be reachable and deletable.

>> destroying itsName, at mem location: 0012FEF4 //**********
>> destroying itsName, at mem location: 0012FF70 //**********
>> What I am interested in here is why the two lines marked (//**********) above do not show that the
>> same area of memory is being deleted twice (which I am now certain *is* happening) ?
Correct, it does not.

>> Or have I used the wrong notation ?
Yes. Hence the above comment.

>> cout << "destroying itsName, at mem location: " << &itsName << endl;
All you're doing here is outputting the address of the local member variable itsName - each class has it's own instance of this, and so will be different.

What you're looking to do is
cout << "destroying itsName, data is at mem location: " << reinterpret_cast<void*>(itsName) << endl;

>> what is the difference between a and &a ?
Hopefuly after reading what's gone before you should be able to answer this one yourself.

a is the address of the data.
&a is the address of the actual variable itself.

>> why *b does not crash the program
Again, data and local variable argument again. Hopefully by now you got it ...

*but* don't do that anyway, a has already been deleted. Subsequent accesses are likely to return garbage.

>> If so, why is the output for *a and *b different ?
On my machine it wasn't. But that's just the luck of the garbage. Mine hadn't been overwritten yet.


Any clearer now ...
Avatar of mrwad99

ASKER

Right I have exhauted this once again.  I have decided to split the points again due to the mass of advice I have assimilated(?).

rstaveley, 25 points for you are here:

http://oldlook.experts-exchange.com/questions/20784957/Points-for-rstaveley.html

This is because you answered the question immediately in your first response to this problem.  I am sure you will agree that _ys_  gave a formidable detailed explanation, so for that reason, _ys_ collect your 50 points here:

http://oldlook.experts-exchange.com/questions/20784958/Points-for-ys.html

Overall a big thank you to everyone who helped me on this; it is much appreciated.

:)