Navigation



9.5 — Overloading unary operators +, -, and !

Overloading unary operators

Unlike the operators you’ve seen so far, the positive (+), negative (-) and logical not (!) operators all are unary operators, which means they only operate on one operand. Because none of these operators change their operands, we will be implementing them as friend functions. All three operands are implemented in an identical manner.

Let’s take a look at how we’d implement operator- on the Cents class we used in a previous example:

class Cents
{
private:
    int m_nCents;

public:
    Cents(int nCents) { m_nCents = nCents; }

    // Overload -cCents
    friend Cents operator-(const Cents &cCents);
};

// note: this function is not a member function!
Cents operator-(const Cents &cCents)
{
    return Cents(-cCents.m_nCents);
}

This should be extremely straightforward. Our overloaded negative operator (-) takes one parameter of type Cents, and returns a value of type Cents.

Here’s another example. The ! operator is often used as a shorthand method to test if something is set to the value zero. For example, the following example would only execute if nX were zero:

if (!nX)
    // do something

Similarly, we can overload the ! operator to work similarly for a user-defined class:

class Point
{
private:
    double m_dX, m_dY, m_dZ;

public:
    Point(double dX=0.0, double dY=0.0, double dZ=0.0)
    {
    m_dX = dX;
    m_dY = dY;
    m_dZ = dZ;
    }

    // Convert a Point into it's negative equivalent
    friend Point operator- (const Point &cPoint);

    // Return true if the point is set at the origin
    friend bool operator! (const Point &cPoint);

    double GetX() { return m_dX; }
    double GetY() { return m_dY; }
    double GetZ() { return m_dZ; }
};

// Convert a Point into it's negative equivalent
Point operator- (const Point &cPoint)
{
    return Point(-cPoint.m_dX, -cPoint.m_dY, -cPoint.m_dZ);
}

// Return true if the point is set at the origin
bool operator! (const Point &cPoint)
{
    return (cPoint.m_dX == 0.0 &&
        cPoint.m_dY == 0.0 &&
        cPoint.m_dZ == 0.0);
}

In this case, if our point has coordinates (0.0, 0.0, 0.0), the logical not operator will return true. Otherwise, it will return false. Thus, we can say:

Point cPoint; // use default contructor to set to (0.0, 0.0, 0.0)

if (!cPoint)
    cout << "cPoint was set at the origin." << endl;
else
    cout << "cPoint was not set at the origin." << endl;

which produces the result:

cPoint was set at the origin.
9.6 — Overloading operators using member functions
Index
9.4 — Overloading the comparison operators

14 comments to 9.5 — Overloading unary operators +, -, and !

  • Shaun

    Is there an operator I can use to overload the usual if() effect? E.g.,

    if (cPoint)
        cout < < "cPoint is not at the origin." << endl;
    else
        cout << "cPoint is at the origin." << endl;
    

    What would happen in this case? Would C++ call the ! operator and reverse the result? Or is there another way to provide a boolean operator?

    • I think you could do this via overloading typecasts. If you provide an overloaded boolean conversion operator for your point, then I think the above example would work. This is covered in lesson 9.10.

      However, personally I wouldn’t recommend implementing it this way. The conversion from point to boolean isn’t very intuitive. I think this is better implemented as a member function that returns a boolean result — in this way, your code will be more self-documenting. eg:

      if (cPoint.IsOrigin())
          // etc
      
      • Adrian

        If I may add a remark: In my opinion Shaun is quite right … If you decide to have an operator!() to check whether a point is non-zero, then it might also be intuitive to have a means of checking the opposite condition.

        But considering the technical point of view, the usual way to do this would be a typecast to void *, even if this looks strange at first sight. This is because a pointer-to-void is perfectly legal as the expression of an if-statement, but it will not be automatically converted to many other things. As a consequence, typing errors are less likely to bite you, because they will produce compiler errors more often.

        A typical example are all std::ios objects, which provide a typecasting operator void *() (and the opposite operator!()) in order to check their state. Consider the stereotypical example, assuming n is an int:

        if (std::cin >> n) { /* n could be read, everything is fine */ }
        if (std::cin << n) { /* this is apparently a typo */ }

        If std::cin had a conversion operator to bool, the second line would in fact compile, but it would certainly not do what you intended.

        • Gurjinder

          There is a problem with the typecast to void* approach. Because the object can be typecast to a void*, delete operator can be invoked on the object now (though the object is on stack):

          Point cPoint;
          delete cPoint;
          

          This can be disastrous!!!

  • [...] 2007 Prev/Next Posts « 9.3 — Overloading the I/O operators | Home | 9.5 — Overloading unary operators +, -, and ! » Thursday, October 4th, 2007 at 5:10 [...]

  • davidv
    if (nX)
        // do something
    

    should be

    
    if (!nX)
        // do something
    

    [ Fixed! Thank you. -Alex ]

  • Kavitha

    Alex,

    I didn’t understand why the object reference is passed as constant.Is there any specific reason behind it?Can you please elaborate.Thank you!

    Point operator- (const Point &cPoint)
    {
        return Point(-cPoint.m_dX, -cPoint.m_dY, -cPoint.m_dZ);
    }
    
    • Making cPoint const means that this function will work with both non-const and const Points. If cPoint were not const, then we would not be able to call it with a const Point because the compiler could not guarantee we would not try to change the value.

      In general, it’s a always good idea to pass your references as const if you are not going to modify the object being referenced. This not only makes it so your function can work with const objects, it also helps ensure you don’t inadvertently change the object, and serves as notification to anyone calling the function that the object being passed in will not be changed.

  • gans

    Hi Alex,

    In the beginning of the section, u have mentioed that friend can be used if the operands are not getting modified. However in this section you re using friend with unary opearotors which changes the operands. please explain.:)

    • integral

      Hi gans,

      He is not actually modifying the operand.
      The following line is a function call to create a new Point object
      Point(-cPoint.m_dX, -cPoint.m_dY, -cPoint.m_dZ)

      My understanding is that when the above function call is made the operator- function copies the each data member of cPoint, decreases it by one and passes this new value using “pass by value” to the Point constructor to create a new object of type Point which is then returned by the operator- function.

      // Convert a Point into it’s negative equivalent
      Point operator- (const Point &cPoint)
      {
      return Point(-cPoint.m_dX, -cPoint.m_dY, -cPoint.m_dZ);
      }

  • [...] 9.5 — Overloading unary operators +, -, and !    9.7 — Overloading the increment and decrement operators [...]

You must be logged in to post a comment.