CPP Musings
- Member functions, Operators and Passing by value
Let us try to do a case study. Consider the multiplication operator(*).
* Member function Vs File Scope function
Consider a class wrapper around the built-in type float. At the minimal you would like to provide arithematic operators for such a class.
class FloatWrapper
{
private:
float value;
public:
FloatWrapper():value(0.0){}
FloatWrapper(float value)
{
this->value =value;
std::cout << "Conversion constructor called\n" ;
}
....
}
Now consider the multiplication operator for the class. The first question. Should that operator be a member or a file-scope function ? Let begin by being object-oriented.
class FloatWrapper
{
private:
float value;
public:
....
const FloatWrapper operator*(const FloatWrapper& rhs) const;
}
....
FloatWrapper a(1.8);
FloatWrapper b(1.9);
FloatWrapper result = a * b // a.operator*(b) called
FloatWrapper result1 = b * a // b.operator*(a) called
FloatWrapper result2 = a * 2.0 // a.operator*(FloatWrapper(2.0)). (implicit //conversion constructor specified in the class called by the compiler.
FloatWrapper result3 = 2.0 * a // duh!!!!! cannot call (2.0).operator*( a)
The operator* is supposed to be commutative and thus this is NOT the way to go. The option is to go for a file-scope function:
...
const FloatWrapper operator*(const FloatWrapper& lhs, const FloatWrapper& rhs);
...
Now this will sweetly preserve the commutative quality of the operator*. Other example of operators which may not be used as filescope are operator<< and operator>>
* Return by value or by reference
We have all heard the when we return/pass by reference, there is gain in efficieny since, no constructors/destructors are called to create the temporary object.
Let us begin by defining the operator* to return a const reference
...
const FloatWrapper& operator*(const FloatWrapper& lhs,
const FloatWrapper& rhs)
{
FloatWrapper result(lhs.value * rhs.value) //Assuming the operator is a friend
return result;
}
What is the problem here
!) We are returning a reference to a locally defined stack variable which will be destroyed upon exiting the function stack
!) Clearly we are returning by reference to avoid making a copy. However, result will have to be constructed just like any other temporary object.
Let us try to construct the object on a heap
...
const FloatWrapper& operator*(const FloatWrapper& lhs,
const FloatWrapper& rhs)
{
FloatWrapper * result = new FloatWrapper(lhs.value * rhs.value) //Assuming the
//operator is a
//friend
return *result;
}
Well,
!) We still have to pay for the constructor call
!) Besides, who will take the ownership of calling delete on this newly created object ? The client ?
The way around this is to make the operator return by value. A good guideline to decide would if you want to return by value or refernce, is identify if you are return an object newly created within the function. Mostly like you want to return by value in such a case.
0 Comments:
Post a Comment
<< Home