8 Replies - 367 Views - Last Post: 24 March 2012 - 02:45 PM Rate Topic: -----

#1 indyarocks  Icon User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 104
  • Joined: 07-March 12

Class function succeeding, even when a return value was not given.

Posted 24 March 2012 - 11:51 AM

Hi,

I'm trying to overload the assignment operator of a class object. Following is the code snippet (Running successfully):
//Assignement operator overloading on a rectangle object

#include <iostream>

using namespace std;

typedef unsigned short int USHORT;

class CRect
{
    public:
    CRect(USHORT l = 1, USHORT w = 1);
    CRect(const CRect &);
    ~CRect();
    void SetL(USHORT l){ *itsL = l;}
    void SetW(USHORT w){ *itsW = w;}
    USHORT GetL() const { return *itsL;}
    USHORT GetW() const { return *itsW;}
    CRect operator= (const CRect&);         //Assignement operator overloading function
    private:
    USHORT *itsL;
    USHORT *itsW;
};
//Define constructor
CRect::CRect(USHORT l , USHORT w)
{
    cout << "\nInside the constructor.\n";
    itsL = new USHORT;
    itsW = new USHORT;
    *itsL = l;
    *itsW = w;
}
//Define deep copy constructor
CRect::CRect(const CRect &rhs)
{
    cout << "\nInside the deep copy constructor.\n";
    itsL = new USHORT;
    itsW = new USHORT;
    *itsL = rhs.GetL();
    *itsW = rhs.GetW();
}
//Define destructor
CRect::~CRect()
{
    cout << "\nInside the destructor.\n";
    delete itsL;
    itsL = 0;
    delete itsW;
    itsW = 0;
}
//Assignement operator overloading function
CRect CRect::operator= (const CRect &rhs)
{
    cout << "\nInside the operator overloading function.\n";
    if( this == &rhs)
    return *this;
    delete itsL;                    //To prevent memory leak of left side object of =
    delete itsW;                    //To prevent memory leak of left side object of =
    itsL = new USHORT;
    itsW = new USHORT;
    *itsL = rhs.GetL();
    *itsW = rhs.GetW();
    return *this;
}

int main()
{
    CRect recta(5,5), rectb;
    cout << "The length and with of recta(5,5) are " << recta.GetL() << " and " << recta.GetW() << endl;
    cout << "The length and with of rectb are " << rectb.GetL() << " and " << rectb.GetW() << endl;
    rectb = recta;
    cout << "The length and with of recta(5,5) are " << recta.GetL() << " and " << recta.GetW() << endl;
    cout << "The length and with of rectb are " << rectb.GetL() << " and " << rectb.GetW() << endl;
    return 0;
}


When I ran this, the output is as below:
Inside the constructor.

Inside the constructor.
The length and with of recta(5,5) are 5 and 5
The length and with of rectb are 1 and 1

Inside the operator overloading function.

Inside the deep copy constructor.

Inside the destructor.
The length and with of recta(5,5) are 5 and 5
The length and with of rectb are 5 and 5

Inside the destructor.

Inside the destructor.

Process returned 0 (0x0)   execution time : 0.007 s
Press any key to continue.


Now coming to my doubt, when I comment out "return *this;" in line 63, i.e when I define operator overloading function as:
//Assignement operator overloading function
CRect CRect::operator= (const CRect &rhs)
{
    cout << "\nInside the operator overloading function.\n";
    if( this == &rhs)
    return *this;
    delete itsL;                    //To prevent memory leak of left side object of =
    delete itsW;                    //To prevent memory leak of left side object of =
    itsL = new USHORT;
    itsW = new USHORT;
    *itsL = rhs.GetL();
    *itsW = rhs.GetW();
//    return *this;
}



Still program is compiling/running successfully.
Inside the constructor.

Inside the constructor.
The length and with of recta(5,5) are 5 and 5
The length and with of rectb are 1 and 1

Inside the operator overloading function.

Inside the destructor.
The length and with of recta(5,5) are 5 and 5
The length and with of rectb are 5 and 5

Inside the destructor.

Inside the destructor.

Process returned 0 (0x0)   execution time : 0.096 s
Press any key to continue.




Actually, earlier I had forgotten to put "return *this;", and I was getting unequal number of "constructor" and "destructor" as shown in above output. Later I noticed that I forgot to put return here. Now, I have two doubts related to this program:
1. How did the program succeed, when there was no return statement, though it was supposed to return a CRect object ?
2. Suppose I did not give return in line 63, then why there is mismatch in number of constructor and distructors in my last run results ? As, I had put "cout" for each kind of constructor and destructor ?

Is This A Good Question/Topic? 0
  • +

Replies To: Class function succeeding, even when a return value was not given.

#2 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 687
  • View blog
  • Posts: 2,376
  • Joined: 31-December 10

Re: Class function succeeding, even when a return value was not given.

Posted 24 March 2012 - 12:55 PM

The assignment operator should return a reference to the class. The keyword this is just a pointer to the class. So when you have the return *this;, that basically reads "return a dereferenced pointer to this class". A dereferenced pointer can be used to initialize a reference so that's why the return statement is supposed be a reference to an object of that class.

The reason you should return a reference from the assignment operator or some other operators is to make it possible for the user to chain operator/function calls together. That's how the iostream's library works for the << && >> operators. They return a std::istream& or std::ostream& depending on the call and that's why you can have the line:
std::cout << "Hello " << name << std::endl;



*EDIT*: The reason your program works if you comment out the return call is because you are returning by value and not by reference. Change it to a reference and comment out the return statement and see what happens.

This post has been edited by vividexstance: 24 March 2012 - 12:59 PM

Was This Post Helpful? 0
  • +
  • -

#3 indyarocks  Icon User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 104
  • Joined: 07-March 12

Re: Class function succeeding, even when a return value was not given.

Posted 24 March 2012 - 01:05 PM

View Postvividexstance, on 24 March 2012 - 12:55 PM, said:

The assignment operator should return a reference to the class. The keyword this is just a pointer to the class. So when you have the return *this;, that basically reads "return a dereferenced pointer to this class". A dereferenced pointer can be used to initialize a reference so that's why the return statement is supposed be a reference to an object of that class.

The reason you should return a reference from the assignment operator or some other operators is to make it possible for the user to chain operator/function calls together. That's how the iostream's library works for the << && >> operators. They return a std::istream& or std::ostream& depending on the call and that's why you can have the line:
std::cout << "Hello " << name << std::endl;



*EDIT*: The reason your program works if you comment out the return call is because you are returning by value and not by reference. Change it to a reference and comment out the return statement and see what happens.

Thanks for the reply vivid. I understand that if I call by reference, the program will fail. But my doubt is, why it doesn't fail when I return by value ? And actually its picking up the right value, even when I dont have return in line 63.
And my other doubt is:

Quote

Suppose I did not give return in line 63, then why there is mismatch in number of constructor and distructors in my last run results ? As, I had put "cout" for each kind of constructor and destructor ?

Was This Post Helpful? 0
  • +
  • -

#4 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 687
  • View blog
  • Posts: 2,376
  • Joined: 31-December 10

Re: Class function succeeding, even when a return value was not given.

Posted 24 March 2012 - 01:11 PM

The answer to your "doubt" is that when you return by value the compiler will create a temporary copy of the object and return it. This means you will have more constructors/destructors called.
Was This Post Helpful? 0
  • +
  • -

#5 indyarocks  Icon User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 104
  • Joined: 07-March 12

Re: Class function succeeding, even when a return value was not given.

Posted 24 March 2012 - 01:17 PM

Hmmmm..That's explains a lot..Thanks:)
So, when it creates a temporary copy of object, a constructor is called.right ? But as you can see in my output:

Quote

01 Inside the constructor.
02
03 Inside the constructor.
04 The length and with of recta(5,5) are 5 and 5
05 The length and with of rectb are 1 and 1
06
07 Inside the operator overloading function.
08
09 Inside the destructor.
10 The length and with of recta(5,5) are 5 and 5
11 The length and with of rectb are 5 and 5
12
13 Inside the destructor.
14
15 Inside the destructor.
16
17 Process returned 0 (0x0) execution time : 0.096 s
18 Press any key to continue.


There is no constructor corresponding to destructor in line 09. I couldn't understand this behavior, because I have defined all the default constructors explicitly with "cout".
Was This Post Helpful? 0
  • +
  • -

#6 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1833
  • View blog
  • Posts: 4,927
  • Joined: 27-December 05

Re: Class function succeeding, even when a return value was not given.

Posted 24 March 2012 - 02:03 PM

The answer is that prior to C++11, it was not "officially" an error when a non-void function did not return a value. Most compilers would generate a warning message but would still compile. This is usually not a problem with simple data types (int, char, etc.). But the behavior with user-defined types in that situation is undefined. If you compile that same program with the g++ compiler, it will crash when the operator= returns.

You saw the reason when you ran it -- there were only 2 objects but the destructor was called 3 times, meaning that there was an attempt to de-allocate memory that hadn't been allocated. Your compiler let you get away with that. Some other compilers won't.

According to the draft C++ standard N3242, Annex C:

Quote

6.6.3
Change: It is now invalid to return (explicitly or implicitly) from a function which is declared to return a
value without actually returning a value
Rationale: The caller and callee may assume fairly elaborate return-value mechanisms for the return of
class objects. If some flow paths execute a return without specifying any value, the implementation must
embody many more complications. Besides, promising to return a value of a given type, and then not
returning such a value, has always been recognized to be a questionable practice, tolerated only because
very-old C had no distinction between void functions and int functions.


Either way, it's wrong. The operator should be defined to return a CRect&, not a CRect.
Was This Post Helpful? 1
  • +
  • -

#7 indyarocks  Icon User is offline

  • D.I.C Head

Reputation: 0
  • View blog
  • Posts: 104
  • Joined: 07-March 12

Re: Class function succeeding, even when a return value was not given.

Posted 24 March 2012 - 02:10 PM

Thanks Stilt. :)

View Postr.stiltskin, on 24 March 2012 - 02:03 PM, said:

Either way, it's wrong. The operator should be defined to return a CRect&, not a CRect.

But we do implement postfix (such as i++, where i is an object) without giving reference, since we want to return the value of i before it gets incremented. I think returning an object by value is as correct as returning it by reference (Keeping performance aside).
Was This Post Helpful? 0
  • +
  • -

#8 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 687
  • View blog
  • Posts: 2,376
  • Joined: 31-December 10

Re: Class function succeeding, even when a return value was not given.

Posted 24 March 2012 - 02:14 PM

Quote

I think returning an object by value is as correct as returning it by reference (Keeping performance aside).

No it is not correct in certain situations, like the assignment operator. In the post increment operator, you must get a temporary copy of the object before you actually increment it and then return the temp object. That's why the return is by value.

This post has been edited by vividexstance: 24 March 2012 - 02:15 PM

Was This Post Helpful? 0
  • +
  • -

#9 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1833
  • View blog
  • Posts: 4,927
  • Joined: 27-December 05

Re: Class function succeeding, even when a return value was not given.

Posted 24 March 2012 - 02:45 PM

View Postindyarocks, on 24 March 2012 - 04:10 PM, said:

But we do implement postfix (such as i++, where i is an object) without giving reference, since we want to return the value of i before it gets incremented. I think returning an object by value is as correct as returning it by reference (Keeping performance aside).

It has nothing to do with performance. It has to do with correct behavior. In post-increment, yes, you return a temporary object because that's what's required for postincrement. On the other hand, for preincrement, you return a reference to the LHS object itself, not a temporary object.

Try a program that does this with three objects:
(a = b) = c

If your assignment operator returns a reference, you should end up with
a == c.
If it returns a temporary object, you will end up with
a == b, and the last assignment "= c" just evaporates into thin air because the object of the assignment is the temporary object which immediately disappears.

(I didn't test this before posting so I could be wrong, but I don't think so.)

This post has been edited by r.stiltskin: 24 March 2012 - 02:46 PM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1