What is the difference between a stand-alone and a member-function operator?

Recommended Answers

All 7 Replies

Most operators (except for the assignment, compound operator-assignment, and the few special operators like & and ->) can be implemented as a free function, not a member function of the class. Operator overloading should be regarded exactly as any other kind of function, they just have a different calling syntax. So, just like you could make any function a member function, you also have the choice to make them free functions. If you understand the difference between a member function and a free function, in the context of any "normal" function, the same applies to operator overloading. However, generally speaking, it is preferable to implement most operator overloads as free functions, because of the way C++ picks the right overloaded version.

Most operators (except for the assignment, compound operator-assignment, and the few special operators like & and ->) can be implemented as a free function, not a member function of the class. Operator overloading should be regarded exactly as any other kind of function, they just have a different calling syntax. So, just like you could make any function a member function, you also have the choice to make them free functions. If you understand the difference between a member function and a free function, in the context of any "normal" function, the same applies to operator overloading. However, generally speaking, it is preferable to implement most operator overloads as free functions, because of the way C++ picks the right overloaded version.

I noticed in a program from my notes they add "friend" in front of the operator. Is that considered a free function since you need friend to access the private members?

Here's a gist of the program.

Length(int feet, int inches)
    {
        setLength(feet, inches);
    }
    Length(int inches){ len_inches = inches; }
    int getFeet(){ return len_inches / 12; }
    int getInches() { return len_inches % 12; }
    void setLength(int feet, int inches)
    {
        len_inches = 12 *feet + inches;
    }
    friend Length operator+(Length a, Length b);
    friend Length operator-(Length a, Length b);
    friend bool operator< (Length a, Length b);
    friend bool operator== (Length a, Length b);

Yes, a line like this:

friend Length operator+(Length a, Length b);

in the declaration of class Length, tells the compiler that later, there is a free function (which is the operator+) that takes two Length parameters, and that this free function should be granted access to the private members of class Length. This what you typically do for these operator overloads. Later, the definition of the function will clearly show that this function is a free function. Like, for instance, if setLength() and operator+() definitions were to appear outside the class declaration, they would look like this:

void Length::setLength(int feet, int inches)  //notice the Length:: because setLength is a member of Length.
{
  len_inches = 12 *feet + inches;
};

Length operator+(Length a, Length b) { //notice no Length:: or 'friend' keyword.
  //...
};

Yes, a line like this:

friend Length operator+(Length a, Length b);

in the declaration of class Length, tells the compiler that later, there is a free function (which is the operator+) that takes two Length parameters, and that this free function should be granted access to the private members of class Length. This what you typically do for these operator overloads. Later, the definition of the function will clearly show that this function is a free function. Like, for instance, if setLength() and operator+() definitions were to appear outside the class declaration, they would look like this:

void Length::setLength(int feet, int inches)  //notice the Length:: because setLength is a member of Length.
{
  len_inches = 12 *feet + inches;
};

Length operator+(Length a, Length b) { //notice no Length:: or 'friend' keyword.
  //...
};

A few questions about this program(full)

#ifndef _LENGTH_H
#define	_LENGTH_H
#include <iostream>
using namespace std;

class Length
{
private:
    int len_inches;
public:
    Length(int feet, int inches)
    {
        setLength(feet, inches);
    }
    Length(int inches){ len_inches = inches; }
    int getFeet(){ return len_inches / 12; }
    int getInches() { return len_inches % 12; }
    void setLength(int feet, int inches)
    {
        len_inches = 12 *feet + inches;
    }
    friend Length operator+(Length a, Length b);
    friend Length operator-(Length a, Length b);
    friend bool operator< (Length a, Length b);
    friend bool operator== (Length a, Length b);   
};
#endif

#include "Length.h"

//*************************************
// Overloaded operator +              *
//*************************************
Length operator+(Length a, Length b)
{
    return Length(a.len_inches + b.len_inches);
}

//*************************************
// Overloaded  operator -             *
//*************************************
Length operator-(Length a, Length b)
{
    return Length(a.len_inches - b.len_inches);
}

//************************************
// Overloaded operator ==            *
//************************************
bool operator==(Length a, Length b)
{
    return a.len_inches == b.len_inches;
}

//************************************
// Overloaded operator <             *
//************************************
bool operator<(Length a, Length b)
{
    return a.len_inches < b.len_inches;
}

int main()
{
    Length first(0), second(0), third(0);
    int f, i;
    cout << "Enter a distance in feet and inches: ";
    cin  >> f >> i;
    first.setLength(f, i);
    cout << "Enter another distance in feet and inches: ";
    cin  >> f >> i;
    second.setLength(f, i);

    // Test the + and - operators
    third = first + second;
    cout << "first + second = ";
    cout << third.getFeet() << " feet, ";
    cout << third.getInches() << " inches.\n";
    third = first - second;
    cout << "first - second = ";
    cout << third.getFeet() << " feet, ";
    cout << third.getInches() << " inches.\n";

    // Test the relational operators
    cout << "first == second = ";
    if (first == second) cout << "true"; else cout << "false";
    cout << "\n";
    cout << "first < second = ";
    if (first < second) cout << "true"; else cout << "false";
    cout << "\n";
        
    return 0;
}

How do things like third.getfeet and third.getInches work? I mean don't third already have a value since it equals to first + second? Also in the length.cpp part, why did they include the class name when they were returning a value(lines 36,44,52,60)? Last question, do you always have to set the value to 0(line 65)?

Also in the length.cpp part, why did they include the class name when they were returning a value(lines 36,44,52,60)?

If you look to the function definition or declaration:

Length operator+(Length a, Length b)

Your return type for the function is a class Length... So the reason they include the name is because the operator overload function will compute the math of the two classes passed to it and then makes a new class with the code at 36, 44... etc and returns that. So your assignment statement of

third = first + second;

effectively takes the first and second, adds their length and then makes a new class returns it and sets it equal to third, hence the reason you can call getFeet() and getInches().

Last question, do you always have to set the value to 0(line 65)?

No, you can call it with any numbers, this code shouldn't compile you have two parameters with no default value so when you do length(0) you should at the very least call it with length(0,0). You could fix it with

Length(int feet=0, int inches=0)
    {
        setLength(feet, inches);
    }

but then would just call it with the code Length first,second,third; if you wanted to call the default parameters.

Your big problem here though is that because you specify two parameters when you try to return legnth(a+b) the compiler doesn't know if that is the number of feet or inches.

I would avoid putting namespaces in header files, if multiple people include it in different places your compiler would complain about duplicates.

>>How do things like third.getfeet and third.getInches work?

These are member functions. This means that they have a hidden parameter which refers to the object on which the function was called (in this case, that object is 'third'). When those functions are called, if they use (set or get) the data members of the class they belong to, then those data members are implicitly taken from the object 'third'. I don't know what else I can explain about that.

>>I mean don't third already have a value since it equals to first + second?

Sure, third stores the value of the addition of first and second. The functions getFeet and getInches are used to access the values stored in third, such that they can be printed out.

>>Also in the length.cpp part, why did they include the class name when they were returning a value(lines 36,44,52,60)?

That is an 'explicit construction'. It is just to explicitly create an object of class Length with its value initialized to whatever is between parentheses.

NB: It is not needed in this case, because the author of this piece of code has made a mistake (amongst many others) and did not require the construction of Length from a single parameter to be explicit, in order to avoid unwanted implicit conversions from int to Length. Normally, his constructor should be declared as:

explicit Length(int inches){ len_inches = inches; } //notice 'explicit' keyword.

>>Last question, do you always have to set the value to 0(line 65)?

In this code, the author did not provide a default constructor (constructor that takes no parameters (or default-valued parameters only)). This means that you have to provide at least one parameter (inches) or two (feet, inches) to create an object of class Length. By giving a default value to the single parameter constructor, the author could have allowed for the class to be default-constructed. In other words, he could have made the constructor as follows:

explicit Length(int inches = 0){ len_inches = inches; } //notice '= 0'.

And then, the line 65 could have been, with equivalent effect, the following:

Length first, second, third;

Now, if you are going to use this piece of code to learn things, you should at least start with something that satisfies minimum standards of quality. So, here is a revised version:

#ifndef MY_LIB_LENGTH_H    //never start any name with an underscore _ character!!!
#define	MY_LIB_LENGTH_H
//#include <iostream>  //only include what is needed in this file!! iostream is not.
//using namespace std; //never import a namespace in a header file!!!

class Length
{
private:
    int len_inches;
public:
    //always initialize as much as possible in the initialization list (not the body of the constructor)
    Length(int feet, int inches) : len_inches(12*feet + inches) { };
    //make the one-parameter constructor 'explicit', unless you know what you are doing.
    // provide a default constructor when it makes sense.
    explicit Length(int inches = 0) : len_inches(inches) { };
    //member-functions which don't modify the data should be marked as 'const'
    int getFeet() const { //notice const here.
      return len_inches / 12; 
    }
    int getInches() const { //same here.
      return len_inches % 12; 
    }
    void setLength(int feet, int inches) {
        len_inches = 12 *feet + inches;
    }
    //prefer passing by const-reference if you don't need a local copy, just a read-only access.
    friend Length operator+(const Length& a, const Length& b);
    friend Length operator-(const Length& a, const Length& b);
    friend bool operator< (const Length& a, const Length& b);
    friend bool operator== (const Length& a, const Length& b);   
};
#endif

#include "Length.h"

//*************************************
// Overloaded operator +              *
//*************************************
Length operator+(const Length& a, const Length& b)
{
    return Length(a.len_inches + b.len_inches);
}

//*************************************
// Overloaded  operator -             *
//*************************************
Length operator-(const Length& a, const Length& b)
{
    return Length(a.len_inches - b.len_inches);
}

//************************************
// Overloaded operator ==            *
//************************************
bool operator==(const Length& a, const Length& b)
{
    return a.len_inches == b.len_inches;
}

//************************************
// Overloaded operator <             *
//************************************
bool operator<(const Length& a, const Length& b)
{
    return a.len_inches < b.len_inches;
}

>>How do things like third.getfeet and third.getInches work?

These are member functions. This means that they have a hidden parameter which refers to the object on which the function was called (in this case, that object is 'third'). When those functions are called, if they use (set or get) the data members of the class they belong to, then those data members are implicitly taken from the object 'third'. I don't know what else I can explain about that.

>>I mean don't third already have a value since it equals to first + second?

Sure, third stores the value of the addition of first and second. The functions getFeet and getInches are used to access the values stored in third, such that they can be printed out.

>>Also in the length.cpp part, why did they include the class name when they were returning a value(lines 36,44,52,60)?

That is an 'explicit construction'. It is just to explicitly create an object of class Length with its value initialized to whatever is between parentheses.

NB: It is not needed in this case, because the author of this piece of code has made a mistake (amongst many others) and did not require the construction of Length from a single parameter to be explicit, in order to avoid unwanted implicit conversions from int to Length. Normally, his constructor should be declared as:

explicit Length(int inches){ len_inches = inches; } //notice 'explicit' keyword.

>>Last question, do you always have to set the value to 0(line 65)?

In this code, the author did not provide a default constructor (constructor that takes no parameters (or default-valued parameters only)). This means that you have to provide at least one parameter (inches) or two (feet, inches) to create an object of class Length. By giving a default value to the single parameter constructor, the author could have allowed for the class to be default-constructed. In other words, he could have made the constructor as follows:

explicit Length(int inches = 0){ len_inches = inches; } //notice '= 0'.

And then, the line 65 could have been, with equivalent effect, the following:

Length first, second, third;

Now, if you are going to use this piece of code to learn things, you should at least start with something that satisfies minimum standards of quality. So, here is a revised version:

#ifndef MY_LIB_LENGTH_H    //never start any name with an underscore _ character!!!
#define	MY_LIB_LENGTH_H
//#include <iostream>  //only include what is needed in this file!! iostream is not.
//using namespace std; //never import a namespace in a header file!!!

class Length
{
private:
    int len_inches;
public:
    //always initialize as much as possible in the initialization list (not the body of the constructor)
    Length(int feet, int inches) : len_inches(12*feet + inches) { };
    //make the one-parameter constructor 'explicit', unless you know what you are doing.
    // provide a default constructor when it makes sense.
    explicit Length(int inches = 0) : len_inches(inches) { };
    //member-functions which don't modify the data should be marked as 'const'
    int getFeet() const { //notice const here.
      return len_inches / 12; 
    }
    int getInches() const { //same here.
      return len_inches % 12; 
    }
    void setLength(int feet, int inches) {
        len_inches = 12 *feet + inches;
    }
    //prefer passing by const-reference if you don't need a local copy, just a read-only access.
    friend Length operator+(const Length& a, const Length& b);
    friend Length operator-(const Length& a, const Length& b);
    friend bool operator< (const Length& a, const Length& b);
    friend bool operator== (const Length& a, const Length& b);   
};
#endif

#include "Length.h"

//*************************************
// Overloaded operator +              *
//*************************************
Length operator+(const Length& a, const Length& b)
{
    return Length(a.len_inches + b.len_inches);
}

//*************************************
// Overloaded  operator -             *
//*************************************
Length operator-(const Length& a, const Length& b)
{
    return Length(a.len_inches - b.len_inches);
}

//************************************
// Overloaded operator ==            *
//************************************
bool operator==(const Length& a, const Length& b)
{
    return a.len_inches == b.len_inches;
}

//************************************
// Overloaded operator <             *
//************************************
bool operator<(const Length& a, const Length& b)
{
    return a.len_inches < b.len_inches;
}

Is this another way of writing the code? The output the same, but the first code I posted looks.. cleaner I suppose? Is that the case?

#ifndef FEETINCHES_H
#define FEETINCHES_H

// The FeetInches class holds distances or measurements 
// expressed in feet and inches.

class FeetInches
{
private:
   int feet;        // To hold a number of feet
   int inches;      // To hold a number of inches
   void simplify(); // Defined in FeetInches.cpp
public:
   
   FeetInches(int f = 0, int i = 0)
      { feet = f;
        inches = i;
        simplify(); }

   void setFeet(int f)       { feet = f; }

   void setInches(int i)
      { inches = i;    simplify(); }

   int getFeet() const     { return feet; }

   int getInches() const    { return inches; }

   FeetInches operator + (const FeetInches &); // Overloaded +
   FeetInches operator - (const FeetInches &); // Overloaded -
};

#endif

// Implementation file for the FeetInches class
#include <cstdlib>       // Needed for abs()
#include "FeetInches.h"

void FeetInches::simplify()
{
   if (inches >= 12)
   {
      feet += (inches / 12);
      inches = inches % 12;
   }
   else if (inches < 0)
   {
      feet -= ((abs(inches) / 12) + 1);
      inches = 12 - (abs(inches) % 12);
   }
}

//**********************************************
// Overloaded binary + operator.               *
//**********************************************

FeetInches FeetInches::operator + (const FeetInches &right)
{
   FeetInches temp;

   temp.inches = inches + right.inches;
   temp.feet = feet + right.feet;
   temp.simplify();
   return temp;
}

//**********************************************
// Overloaded binary - operator.               *
//**********************************************

FeetInches FeetInches::operator -(const FeetInches &right)
{
   FeetInches temp;

   temp.inches = inches - right.inches;
   temp.feet = feet - right.feet;
   temp.simplify();  
   return temp;
}

int main()

int feet, inches;  
FeetInches first, second, third;

   // Get a distance from the user.
   cout << "Enter a distance in feet and inches: ";
   cin >> feet >> inches;

   // Store the distance in the first object.
   first.setFeet(feet);
   first.setInches(inches);

   // Get another distance from the user.
   cout << "Enter another distance in feet and inches: ";
   cin >> feet >> inches;

   // Store the distance in second.
   second.setFeet(feet);
   second.setInches(inches);

   // Assign first + second to third.
   third = first + second;

   // Display the result.
   cout << "first + second = ";
   cout << third.getFeet() << " feet, ";
   cout << third.getInches() << " inches.\n";

   // Assign first - second to third.
   third = first - second;

   // Display the result.
   cout << "first - second = ";
   cout << third.getFeet() << " feet, ";
   cout << third.getInches() << " inches.\n";

return 0;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.