Solved

Overloaded 'friend' assignment operator confusion

Posted on 2003-11-13
25
711 Views
Last Modified: 2008-02-26
Ah hello.

I have been reading a book about C++ and have come across a listing which I don't quite understand how works.  Basically it is a simplified string class.  Let me show you the header file:

#ifndef STRING_H
#define STRING_H

#include <string.h>

class String
{
public:
      String();
      String(const char *const);
      String(const String &);
      ~String();
      char & operator[](int offset);
      char operator[](int offset) const;
      String operator+(const String&);
      friend String operator+(const String&, const String&);
      void operator+=(const String&);
      String & operator= (const String &);
      int GetLen()const { return itsLen; }
      const char * GetString() const { return itsString; }
private:
      String (int);         // private constructor
      char * itsString;
      unsigned short itsLen;
};


#endif // STRING_H

Nothing too unusual there.  The function of interest is

friend String operator+(const String&, const String&);

the body of which is:

String operator+(const String& lhs, const String& rhs)
{
      cout << "String operator+: adding \"" << lhs.GetString() << "\" to \"" << rhs.GetString() << "\"" << endl;
      int  totalLen = lhs.GetLen() + rhs.GetLen();
      String temp(totalLen);
      for (int i = 0; i<lhs.GetLen(); i++)
            temp[i] = lhs[i];
      for (int j = 0; j<rhs.GetLen(); j++, i++)
            temp[i] = rhs[j];
      temp[totalLen]='\0';
      cout << "Result is \"" << temp.GetString() << "\"" << endl;
      return temp;
}

Now my book states that the purpose of this function is to enable us to say things like:

      String s1("String One ");
      String s2("String Two ");
      char* c1 = { "C-String One "; }
      String s5;
      
      //s5 = c1 + s1;
      s5 = s1 + c1; ********
      cout << "s5: " << s5.GetString() << endl;

With particular interest being paid to the fact that we are adding a normal C style string to a String object (********)

I am not particularly uncomfortable with overloading operators, but I cannot see how the function prototype

      friend String operator+(const String&, const String&);

can be similar to

      String operator+(const String&);

in calling terms.  I mean, the latter, when called, is effectively

      lhs.operator+(rhs);

But the former appears to be

      operator+(lhs, rhs);

Why ?!?!?!

Also can someone explain why when I code:

      s5 = c1 + s1;

the normal operator+ is called (ie String operator+(const String&) )

but when I code:

      s5 = s1 + c1;

it is the       (friend String operator+(const String&, const String&) ) one ?


Confused and awaiting an answer.

Thanks in advance.
0
Comment
Question by:mrwad99
  • 14
  • 6
  • 4
  • +1
25 Comments
 
LVL 9

Expert Comment

by:_ys_
ID: 9742022
My advice is to get another book. What book are you reading, and who's the author !?

String operator+(const String& lhs, const String& rhs) is better implemented as:

String operator+(const String& lhs, const String& rhs)
{
    String result = lhs.operator+(rhs);
    return result;
}

Rather than that hideous 'reimplentation' it currently has. Unless of course operator+(const String&) invokes outside of it's own scope into this friend function - laughable.

And as for:
void operator+=(const String&); // *** void ***

Again, what book are you reading, and who's the author !?
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9742051
Hmm,

The book I am reading is Sams Teach yourself C++ in 21 days.

What is wrong with

// changes current string, returns nothing
void String::operator+=(const String& rhs)
{
      unsigned short rhsLen = rhs.GetLen();
      unsigned short totalLen = itsLen + rhsLen;
      String  temp(totalLen);
      for (int i = 0; i<itsLen; i++)
            temp[i] = itsString[i];
      for (int j = 0; j<rhs.GetLen(); j++, i++)
            temp[i] = rhs[i-itsLen];
      temp[totalLen]='\0';
      *this = temp;
}

?

And what are the differences in calling syntax with
    s5 = c1 + s1;

and

    s5 = s1 + c1;

??
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9742055
Sorry author is Jesse Liberty.
0
 
LVL 9

Expert Comment

by:_ys_
ID: 9742256
>Sams Teach yourself C++ in 21 days
They must have taught themselves as well !!

> void String::operator+=(const String& rhs);
Returning a void from operator+= is just bad form. Assignment operators should return self references. It allows for chaining.
a = (b += c);

> s5 = c1 + s1;
I never seen operator+= being called for this syntax before. I would have expected the friend function to be used.

> s5 = s1 + c1;
Similarly I would have expected operator+= to be called here, not the friend as you detailed.

I don't have access to a C++ compiler at the moment, so can't test / verify anything.
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9742286
OK.  Since you say you do not have a compiler handy, I will ask questions that do not revolve around testing.

The reason that the operator+ is defined as a friend is .....?

The reason that the friend operator+ takes TWO parameters (and hence does not use the 'this' object) as oposed to its 'unfriendly' operator+ counterpart  is ......?

Cheers.
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9742324
> s5 = c1 + s1;
I never seen operator+= being called for this syntax before. I would have expected the friend function to be used.

> s5 = s1 + c1;
Similarly I would have expected operator+= to be called here, not the friend as you detailed.


??

I am not with you.

I cannot see where += is being called !  It looks to me like a straightforward + !

Since s5 is already empty, I suppose you could say s5+=(s1+c1) but eitherway this += function is *NOT* being called, as my test output shows !  It is the + operator with two parameters that is being called ! (the friendly one)

?!
0
 
LVL 15

Expert Comment

by:efn
ID: 9745344
> The reason that the operator+ is defined as a friend is .....?

...so that you can code a concatenation expression that doesn't have a String as the left-hand operand.  With a numeric kind of addition, it's not so important, but with a string concatenation, the order of the operands matters, because it controls the order in which they appear in the output string.  So it's useful to be able to concatenate two operands into a String even when the first one is not itself a String.

Are you sure you didn't get the functions called backwards in your original question?

With

     s5 = s1 + c1;

I would expect s1.operator + to be called, since the left-hand operand s1 is a String.

With

     s5 = c1 + s1;

I would expect the friend operator + to be called.  Expressions like this are the motivation for having the friend operator.  Without the friend operator, the compiler will just say "I don't know how to add a String to a pointer to char."  With the friend operator, the compiler will figure out that it can convert the char pointer to a String using one of the String constructors, and then it can use the friend operator +.

> The reason that the friend operator+ takes TWO parameters (and hence does not use the 'this' object) as oposed to its 'unfriendly' operator+ counterpart  is ......?

The friend operator + is not a member function.  If it were, it would not need to be declared as a friend; declaring it as a friend automatically makes it not a member of the class.  So it does not use the "this" object because there is no "this" object for it to use.

+ is a binary operator:  it has to have two operands.  When it's a member function, it's called as a member of the left-hand operand and the right-hand operand is its one parameter.  When it's not a member function, both of its operands are parameters, so it takes two parameters.

--efn
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9747328
efn,

Thanks.  You are right about which function is called;

s5 = c1 + s1 results in the friend operator being called; s5 = s1 + c1 calls the normal operator+.

(i)I notice that when I code:

s5 = s1 + c1;

immediately before entering the operator+ body, there is a call to the String(const char *const) constructor.  Now am I correct in assuming that this is the compiler doing its magic and converting c1 to a String object ?  And without this constructor, the code would fail *because it cannot perform the conversion*, correct ?  (I know it *will* fail, I just want to be 100% sure of why).


(ii)I have reread your text about the friendliness factor, but I still do not understand what the 'friend' call does.  I understand that it *has* to be a friend, otherwise it will not compile: for a start the line

String temp(totalLen);

will fail because this is using the private constructor.

I am used to friend functions giving access to a class's private members, such as with the overloaded ouput operator:

class Object
{
public:
//....
friend std::ostream& operator << (std::ostream& os, const Object& obj)    {
   return os << obj.PRIVATE_MEMBER << endl;
}
private:
int PRIVATE_MEMBER;
};

Now this is declaring that the output stream, belonging to std, is a friend of the Object class, so can thus access the PRIVATE_MEMBER variable of obj without problem.  This is as clear as a bell to me.  

But with the + operator in question, I cannot see how this is analogous to the principle above.  Is is just that it allows + to access the private constructor of the String class ?  Or is there more ?

Sorry if I am unclear as to explaining what is puzzling me, it is just awquard to explain.

Thanks again.
0
 
LVL 9

Assisted Solution

by:_ys_
_ys_ earned 50 total points
ID: 9747475
>I cannot see where += is being called !  It looks to me like a straightforward + !
I agree to this as well. Just typos on my part. Sleep deprivation.

>efn, Thanks.  You are right about which function is called;
My sanity and faith are restored.


Just to throw a spanner in the works here ...  as I tend to do a lot ...

In my original post I slammed the authors reimplementation of code [/duplication] and posted this.

String operator+(const String& lhs, const String& rhs)
{
    String result = lhs.operator+(rhs);
    return result;
}

In this case String operator+(const String&, const String&) doesn't need to be declared as a friend at all.


Anyhow, that's not what you're asking about. Back to the Q.

>Now am I correct in assuming that this is the compiler doing its magic and converting c1 to a String object ?
Yes. Because the only function it has at hand is operator+(const String&) - but it's looking for operator+(const char* &). It can however go from a const char* to const String using an available construcctor String(const char* const).

>And without this constructor, the code would fail *because it cannot perform the conversion*, correct ?
Yes, but be careful what you saying. The code can still fail when this constructor is still present - by adding the explicit keyword to it.

>Is is just that it allows + to access the private constructor of the String class ?  Or is there more ?
Me throwing my spanner in the works, above, pretty much sums this one up for you.
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9747550
String operator+(const String& lhs, const String& rhs)
{
   //cannot convert 'this' pointer from 'const class String' to 'class String &' error below
   String result = lhs.operator+(rhs);
   return result;
}


This does not compile.

I try removing the friend declaration from the header file, whilst leaving the above the same, and get even more errors.

I am not going to post all of this code here, please get it from http://homepage.ntlworld.com/d.billingham/TEST/

so you can see yourself what is going on.


>>Me throwing my spanner in the works, above, pretty much sums this one up for you.

Totally lost.

?!?!?
0
 
LVL 9

Expert Comment

by:_ys_
ID: 9747588
>cannot convert 'this' pointer from 'const class String' to 'class String &' error below
Sorry about that.

String operator+(const String& lhs, const String& rhs)
{
   String result(lhs);
   return result.operator+(rhs);
}

Definitely helps to have a compiler at hand.

>Me throwing my spanner in the works, above, pretty much sums this one up for you.
>Totally lost.
Ok. I was just trying to say that a friend in this case is not neccessary. It would have solved a lot of your mysteries.
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9747800
String operator+(const String&, const String&);

instead of

friend String operator+(const String&, const String&);

generates a further 3 errors:

1) binary 'operator +' has too many parameters

2) class std::reverse_iterator<_RI,_Ty,_Rt,_Pt,_D> __cdecl std::operator +(_D,const class std::reverse_iterator<_RI,_Ty,_Rt,_Pt,_D> &)' : could not deduce template argument for '' from 'char *'

3) binary '+' : no global operator defined which takes type 'class String' (or there is no acceptable conversion)

???
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 19

Author Comment

by:mrwad99
ID: 9747816
Also what did the compiler mean with:

cannot convert 'this' pointer from 'const class String' to 'class String &' error

What was it trying to convert ?  I figured it would work looking at it since I am passing in a const String& to the first +, and then passing the same thing on to the second + !

Cheers again.
0
 
LVL 15

Accepted Solution

by:
efn earned 95 total points
ID: 9749947
mrwad99>And without this constructor, the code would fail *because it cannot perform the conversion*, correct ?
_ys_>Yes, but be careful what you saying. The code can still fail when this constructor is still present - by adding the explicit keyword to it.

When you have a String object to the left of a + operator, the compiler will figure you want to call a String::operator + function.  There may be more than one in the String class, although there is only one in your example.  If it finds one where the parameter type matches the type of the expression to the right of the + operator, it will use that operator function.  If it can't find a match, it will try to convert the right-hand operand to a type it can pass to one of the operator + functions.  So technically, it would fail because there is no operator + function that takes a parameter of type char* AND there is no conversion available.

> But with the + operator in question, I cannot see how this is analogous to the principle above.  Is is just that it allows + to access the private constructor of the String class ?

Yes, I think that is the only private member that the friend function accesses.  So the function needed to be a friend with its current implementation.  However, there is enough functionality available in the class's public interface that it could have been implemented using that interface alone, so assuming the implementation can be changed, I agree with _ys_ that it didn't have to be a friend.

> String operator+(const String&, const String&);

> instead of

> friend String operator+(const String&, const String&);

> generates a further 3 errors:

Remember how I said above that the "friend" keyword made the function a non-member of the class?  You got the errors because removing the "friend" keyword made the function a member of the String class.  As a member operator + function, *this is the left operand and the operator function should take only one parameter.  If you want to experiment with implementing the operator as a non-friend, you should not only remove the "friend" keyword, but also move it outside of the class declaration.

>    //cannot convert 'this' pointer from 'const class String' to 'class String &' error below
>    String result = lhs.operator+(rhs);
> Also what did the compiler mean with:

> cannot convert 'this' pointer from 'const class String' to 'class String &' error

The lhs argument is declared const, so you have promised not to change it.  The compiler is concerned that calling lhs.operator + might change lhs, because the member operator + function is not declared const.  The function shouldn't change the object, but it doesn't promise not to, so the compiler objects.  So assuming the function does not in fact change the object, you could have avoided the error messages by changing the declaration of the operator + function to:

String operator+(const String&) const;

_ys_'s fix will also work, because it makes a non-const copy of lhs and calls the operator function on the copy.

--efn
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9750368
efn,

OK.  I reread what you said a few times and then applied it.  Firstly, about the friend thing - where you say remove it from the class, I commented out the definition of

friend String operator+(const String&, const String&);

and moved the body of it into my test program.  Now the code compiles when it is not declared as a friend.  This is what you meant by moving it, yes ?

Secondly,

Regarding the issue about making the said function const:

friend String operator+(const String&, const String&) const;

with body:

String operator+(const String& lhs, const String& rhs) const
{
      return rhs + lhs;
}

Right ?  Now I changed this as you can see, in both the .h and .cpp files, and it still will not compile:


error C2270: '+' : modifiers not allowed on nonmember functions


Interestingly I played with the none-friend version, and this compiles fine:

String operator+(const String& lhs, const String& rhs)
{
      return rhs + lhs;
}

Note that this is not const.  Why does this work when it is not const and when it is not a member function, yet does not work when it is a member function and is const ?

Still confused, but feeling we are getting somewhere...
0
 
LVL 15

Expert Comment

by:efn
ID: 9750803
> This is what you meant by moving it, yes ?

Not exactly, but it's probably close enough for your purposes.  Better form would be to declare the function in a header file, put the implementation in a separately compiled source file, include the header file in the test program source, and link the implementation object file into the test program.  But you did achieve the goal of making it a non-member of the class.

> Why does this work when it is not const and when it is not a member function, yet does not work when it is a member function and is const ?

A function that is not a member of a class is called a "free function."  It's meaningless for a free function to be declared const, because there is no object for it not to modify.  You tried to put the const qualifier on the free function with two parameters.  If you scrutinize my previous comment carefully, you will see that I was advising you to put the const qualifier on the member function, the one with only one parameter, not the free function.

I'm surprised that the last function you showed compiled.  I would have expected a complaint from the compiler, since rhs is const and rhs.operator + is not.

By the way, having operator + return rhs + lhs is not a good idea.  When you have a string x with a value of "a" and a string y with a value of "b", you want x + y to be "ab", but if the operator returns rhs + lhs, you will get "ba".

--efn
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9754331
OK that is great.

Sorry I did misread your comment efn and now everything works.  Phew.  However:

If I code

String operator+(const String& lhs, const String& rhs)
{
      cout << "Two parameters" << endl;
      return lhs + rhs;
}

instead of

String operator+(const String& lhs, const String& rhs)
{
      cout << "Two parameters" << endl;
      return lhs.operator +(rhs);
}

The compiler complains that the operator+ is ambiguous.  Now I am correct in thinking that this is *because there are two + functions; the one above and the one that takes one parameter*, yes?

I have increased the points for this Q to 100 as I am going to split them 50/50 between you two, but I will give a further 25 to anyone who can solve this (string related) question:

Say I have an employee class, that basically holds info about a person, and this class has three String objects:

class Employee
{
public:
      Employee();
      Employee(char *, char *, char *, long);
      ~Employee();
      void SetFirstName(const String & fName);
private:
      String itsFirstName;
      String itsLastName;
      String itsAddress;
};

///////////// Employee Functions ///////////////////

Employee::Employee():
      itsFirstName("EMPTY"),
      itsLastName("EMPTY"),
      itsAddress("EMPTY"),
      itsSalary(0)
{}

void Employee::SetFirstName(const String & fName)
{
      cout << "SetFirstName\n";
      itsFirstName = fName;
      cout << "END SetFirstName\n";
}

///////////// String Functions ///////////////////

String::String(const char* const cString)
{
      cout << "String(char*) constructor, constructing " << cString;
      itsLen = strlen(cString);
      itsString = new char[itsLen+1];
      for (int i = 0; i<itsLen; i++) {
            itsString[i] = cString[i];
      }
      itsString[itsLen]='\0';
      cout << ".....constructed\n";
}


String::~String ()
{
      cout << "String destructor, destroying: " << itsString;
      delete [] itsString;
      itsLen = 0;
      cout << ".....destroyed" << endl;
}

// operator equals, frees existing memory then copies string and size
String& String::operator=(const String & rhs)
{
      cout << "String operator=: assigning \"" << rhs.GetString() << "\" to \"" << this->GetString() << "\"";
      if (this == &rhs) {
            return *this;
      }
      cout << "Deleting: " << itsString << endl;
      
      delete [] itsString;
      itsLen=rhs.GetLen();
      itsString = new char[itsLen+1];
      for (int i = 0; i<itsLen;i++) {
            itsString[i] = rhs[i];
      }
      itsString[itsLen] = '\0';

      cout << "......assigned\n";
      return *this;
}

My test program contains:

      Employee e;
      e.SetFirstName("Dave");

      return 0;

Firstly, what does String::String(const char* const cString) make const ?  It seems that there are too many 'const' declarations there to me, but it still compiles.

Secondly, I have put some extra statements into the String methods to clarify what is going on and when.  The line I am interested in is clearly marked with (3) in the output below.

So what is happening essentially is that on construction of the Employee object, the three strings are being created with initial values EMPTY.  Then the string literal "Dave" is being converted to a temporary String object (1).  Ok.  Now SetFirstName is called, which takes a REFERENCE TO THIS TEMPORARY OBJECT, then proceeds to call operator= to assign this temporary to the itsString member variable.  All good so far; the operator= has to delete the current value in itsString which accounts for the deletion in (2).

Now the interest is in (3).  itsString has now been assigned to be a new area of memory with the same value as what was originally in the temporary object.  

*But how does the compiler know to delete the temporary object here, and why has it not previously gone out of scope when it was passed as a reference to (1) SetFirstName and then later (2) Operator= ? *  I have read no end of times not to pass references to temporary objects, yet this is exactly what is happening here !

---- OUTPUT-----

String(char*) constructor, constructing EMPTY.....constructed
String(char*) constructor, constructing EMPTY.....constructed
String(char*) constructor, constructing EMPTY.....constructed
String(char*) constructor, constructing Dave.....constructed <-------------------------(1)
SetFirstName
String operator=: assigning "Dave" to "EMPTY"Deleting: EMPTY......assigned <-----------(2)
END SetFirstName
String destructor, destroying: Dave.....destroyed <------------------------------------(3)
Destroying Employee
String destructor, destroying: EMPTY.....destroyed
String destructor, destroying: EMPTY.....destroyed
String destructor, destroying: Dave.....destroyed
0
 
LVL 15

Expert Comment

by:efn
ID: 9754704
> The compiler complains that the operator+ is ambiguous.  Now I am correct in thinking that this is *because there are two + functions; the one above and the one that takes one parameter*, yes?

I don't think so.  When you have an object of a user-defined class to the left of the + sign, the compiler should just call the member function and not look for a free function.  I would have to see more of the current state of your code to figure out where the ambiguity is.

> Firstly, what does String::String(const char* const cString) make const ?

The cString parameter is a pointer to one or more characters.  The first "const" says that the constructor is not allowed to change any of the characters by using this pointer.  The second "const" says that the constructor is not allowed to change the pointer argument itself so it points somewhere else.  The second "const" is meaningful, but rather pointless, since the pointer is passed by value and if the constructor changes it, the constructor is only changing its own copy of the value and not affecting anything else.

> But how does the compiler know to delete the temporary object here, and why has it not previously gone out of scope when it was passed as a reference to (1) SetFirstName and then later (2) Operator= ?

The compiler knows that it is constructing the temporary String object just to pass to the SetFirstName function, so it knows that when control returns from that function, the temporary object is not needed any more, so it can be destroyed.  To SetFirstName and operator =, the object is just an argument, so they have no responsibility for destroying it.

> I have read no end of times not to pass references to temporary objects, yet this is exactly what is happening here !

I think it makes a difference whether the reference is to a constant object.  If you change the declaration of SetFirstName to

void Employee::SetFirstName(String & fName)

the compiler should complain, because now it looks like SetFirstName might want to change fName, but that is pointless if fName is a temporary object.  With the code you have, SetFirstName can't do anything but look at fName and the compiler can manage its lifetime, so there is no problem.

--efn
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9754847
>The first "const" says that the constructor is not allowed to change any of the characters by using this pointer.  The second "const" says that the constructor is not allowed to change the pointer argument itself so it points somewhere else.

Ok, I thought though that if the constructor was not allowed to change anything via 'this', then the *function itself* would be const, i.e.

String::String((1)const char * (2)const cString) (3)const  {
}

Now, const (3) would surely be saying that the function itself is constant and cannot change 'this'.  It was just const (1) and (2) that were in the function questioned though.  const(1) is saying that the char* cannot be altered; yes that is futile, but what is const (2) saying ?

Or is const (1) saying that the pointer (to the variable, cString, that is constant) cannot be changed ?

-- Almost there...
0
 
LVL 15

Expert Comment

by:efn
ID: 9754943
Maybe this will help:

String::String(const char * const cString)
{
  *cString = 'w';  // Not allowed by const (1), perfectly OK with const (2)
  cString = "nataka tembo baridi";      // Not allowed by const (2), perfectly OK with const (1)
}

--efn
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9754976
I see, it is the pointer that is constant and also the variable itself, right ?

Sorry, in the aid of my first question I post all of my String.cpp code as requested to see where the ambiguity in + is:

///////////////////////////////////////////////

#include "String.h"
#include <iostream>

using namespace std;


// default constructor creates string of 0 bytes

String::String()
{
      cout << "Default string constructor\n";
      itsString = new char[1];
      itsString[0] = '\0';
      itsLen=0;
}


// private (helper) constructor, used only by
// class methods for creating a new string of
// required size.  Null filled.

String::String(int len)
{
      //cout << "\tString(int) constructor\n";
      itsString = new char[len+1];
      for (int i = 0; i<=len; i++)
            itsString[i] = '\0';
            itsLen=len;
}


// Converts a character array to a String

String::String(const char * const cString)
{
      cout << "String(char*) constructor, constructing " << cString;
      itsLen = strlen(cString);
      itsString = new char[itsLen+1];
      for (int i = 0; i<itsLen; i++) {
            itsString[i] = cString[i];
      }
      itsString[itsLen]='\0';
      cout << ".....constructed\n";
}

// copy constructor

String::String (const String & rhs)
{
      cout << "String(String&) constructor\n";
      itsLen=rhs.GetLen();
      itsString = new char[itsLen+1];
      for (int i = 0; i<itsLen;i++)
            itsString[i] = rhs[i];
      itsString[itsLen] = '\0';
}


// destructor, frees allocated memory

String::~String ()
{
      cout << "String destructor, destroying: " << itsString;
      delete [] itsString;
      itsLen = 0;
      cout << ".....destroyed" << endl;
}


String operator+(const String& lhs, const String& rhs)
{
      cout << "Two parameters" << endl;
      return lhs.operator +(rhs);
}


// creates a new string by adding current
// string to rhs

String String::operator+(const String& rhs) const
{
      cout << "1 param" << endl;
      int  totalLen = itsLen + rhs.GetLen();
      String temp(totalLen);
      int i, j;
      for (i = 0; i<itsLen; i++)
            temp[i] = itsString[i];
      for (j = 0; j<rhs.GetLen(); j++, i++)
            temp[i] = rhs[j];
      temp[totalLen]='\0';
      return temp;
}


// operator equals, frees existing memory
// then copies string and size

String& String::operator=(const String & rhs)
{
      cout << "String operator=: assigning \"" << rhs.GetString() << "\" to \"" << this->GetString() << "\"";
      if (this == &rhs)
            return *this;
      cout << "Deleting: " << itsString << endl;
      
      delete [] itsString;
      itsLen=rhs.GetLen();
      itsString = new char[itsLen+1];
      for (int i = 0; i<itsLen;i++) {
            itsString[i] = rhs[i];
      }
      itsString[itsLen] = '\0';

      cout << "......assigned\n";
      return *this;
}


//non constant offset operator, returns
// reference to character so it can be
// changed!

char & String::operator[](int offset)
{
      if (offset > itsLen)
            return itsString[itsLen-1];
      else
            return itsString[offset];
}


// constant offset operator for use
// on const objects (see copy constructor!)

char String::operator[](int offset) const
{
      if (offset > itsLen)
            return itsString[itsLen-1];
      else
            return itsString[offset];
}



// changes current string, returns nothing

void String::operator+=(const String& rhs)
{
      unsigned short rhsLen = rhs.GetLen();
      unsigned short totalLen = itsLen + rhsLen;
      String  temp(totalLen);
      for (int i = 0; i<itsLen; i++)
            temp[i] = itsString[i];
      for (int j = 0; j<rhs.GetLen(); j++, i++)
            temp[i] = rhs[i-itsLen];
      temp[totalLen]='\0';
      *this = temp;
}

///////////////////////////////////////////////

Also why if I call the destructor on an Employee object explicitly, my code crashes:

      Employee e;
      e.SetFirstName("Dave");
      ~Employee();

Because it tries to destroy the same employee object twice and calling delete on the String components makes it break.

Why is it trying to delete it twice ?


*I know I am going on here but I dont see the point in asking another separate question when you know what is going on already.*

I will increase the points again of course !
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9754994
typo:

e.~Employee();

not

~Employee()
0
 
LVL 17

Expert Comment

by:rstaveley
ID: 9755358
Dont call the destructor. The destructor is automatically called when you leave the scope if you instantiate the class as an automatic variable.

Try this:
--------8<--------
#include <iostream>

struct X {
X() {std::cout << "Now you see me\n";}
~X() {std::cout << "Now you don\'t\n";}
};

int main()
{
    std::cout << "About to enter a local scope\n";

    // These curly braces delimit a local scope
    {
        X x;
    }

    std::cout << "Just left the local scope\n";
}
--------8<--------
0
 
LVL 15

Expert Comment

by:efn
ID: 9755373
> I see, it is the pointer that is constant and also the variable itself, right ?

Yes, the "const" to the left of the * refers to what the pointer addresses and the "const" to the right of the * refers to the pointer itself.

With regard to the ambiguity, it looks like I was wrong about the member function getting preference.  So it was ambiguity between the member function and the free function, as you thought.

> Why is it trying to delete it twice ?

It's not:  you are destroying it once and then the compiler is automatically trying to destroy it again.  Only in very special and obscure cases do you need to call a destructor explicitly.  Most of the time, it gets called automatically.  I don't think I've ever needed to code an explicit destructor call in all my C++ experience.

--efn
0
 
LVL 19

Author Comment

by:mrwad99
ID: 9758214
Right I have doubled the original 50 points that were on offer to 100, and split them 50/50 between _ys_ and efn.  Also I have given efn 25 more for the explanation of the string destruction, and a further 20 for the destructor query.

rstaveley thanks for that - it was not telling me anything I did not know since I was not aware that the compiler tries to delete the object even after explicitly destroying it, which efn explained.

I always seem to learn so much more than was originally intended when I ask a question here - I really do appreciate everything given from everyone here !

Many many thanks to all.

:)
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

Templates For Beginners Or How To Encourage The Compiler To Work For You Introduction This tutorial is targeted at the reader who is, perhaps, familiar with the basics of C++ but would prefer a little slower introduction to the more ad…
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now