• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 440
  • Last Modified:

array class operator oveloading

How can I add three more additional operators for the following program.

// Fig. 11.8: fig11_08.cpp
// Array class test program.
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
 
#include "Array.h"
 
int main()
{
   Array integers1( 7 ); // seven-element Array
   Array integers2; // 10-element Array by default
 
   // print integers1 size and contents
   cout << "Size of Array integers1 is " 
      << integers1.getSize()
      << "\nArray after initialization:\n" << integers1;
 
   // print integers2 size and contents
   cout << "\nSize of Array integers2 is " 
      << integers2.getSize()
      << "\nArray after initialization:\n" << integers2;
 
   // input and print integers1 and integers2
   cout << "\nEnter 17 integers:" << endl;
   cin >> integers1 >> integers2;
 
   cout << "\nAfter input, the Arrays contain:\n"
      << "integers1:\n" << integers1
      << "integers2:\n" << integers2;
 
   // use overloaded inequality (!=) operator
   cout << "\nEvaluating: integers1 != integers2" << endl;
 
   if ( integers1 != integers2 )
      cout << "integers1 and integers2 are not equal" << endl;
 
   // create Array integers3 using integers1 as an
   // initializer; print size and contents
   
   
   
   
   
   Array integers3( integers1 ); // invokes copy constructor
 
   cout << "\nSize of Array integers3 is "
      << integers3.getSize()
      << "\nArray after initialization:\n" << integers3;
 
   // use overloaded assignment (=) operator
   cout << "\nAssigning integers2 to integers1:" << endl;
   integers1 = integers2; // note target Array is smaller
 
   cout << "integers1:\n" << integers1
      << "integers2:\n" << integers2;
 
   // use overloaded equality (==) operator
   cout << "\nEvaluating: integers1 == integers2" << endl;
 
   if ( integers1 == integers2 )
      cout << "integers1 and integers2 are equal" << endl;
 
   // use overloaded subscript operator to create rvalue
   cout << "\nintegers1[5] is " << integers1[ 5 ];
 
   // use overloaded subscript operator to create lvalue
   cout << "\n\nAssigning 1000 to integers1[5]" << endl;
   integers1[ 5 ] = 1000;
   cout << "integers1:\n" << integers1;
 
   // attempt to use out-of-range subscript
   cout << "\nAttempt to assign 1000 to integers1[15]" << endl;
   integers1[ 15 ] = 1000; // ERROR: out of range
   return 0;
} // end main
 
/**************************************************************************
 * (C) Copyright 1992-2005 by Deitel & Associates, Inc. and               *
 * Pearson Education, Inc. All Rights Reserved.                           *
 *                                                                        *
 * DISCLAIMER: The authors and publisher of this book have used their     *
 * best efforts in preparing the book. These efforts include the          *
 * development, research, and testing of the theories and programs        *
 * to determine their effectiveness. The authors and publisher make       *
 * no warranty of any kind, expressed or implied, with regard to these    *
 * programs or to the documentation contained in these books. The authors *
 * and publisher shall not be liable in any event for incidental or       *
 * consequential damages in connection with, or arising out of, the       *
 * furnishing, performance, or use of these programs.                     *
 **************************************************************************/
 
 
 
// Fig 11.7: Array.cpp
// Member-function definitions for class Array
#include <iostream>
using std::cerr;
using std::cout;
using std::cin;
using std::endl;
 
#include <iomanip>
using std::setw;
 
#include <cstdlib> // exit function prototype
using std::exit;
 
#include "Array.h" // Array class definition
 
// default constructor for class Array (default size 10)
Array::Array( int arraySize )
{
   size = ( arraySize > 0 ? arraySize : 10 ); // validate arraySize
   ptr = new int[ size ]; // create space for pointer-based array
 
   for ( int i = 0; i < size; i++ )
      ptr[ i ] = 0; // set pointer-based array element
} // end Array default constructor
 
// copy constructor for class Array;
// must receive a reference to prevent infinite recursion
Array::Array( const Array &arrayToCopy ) 
   : size( arrayToCopy.size )
{
   ptr = new int[ size ]; // create space for pointer-based array
 
   for ( int i = 0; i < size; i++ )
      ptr[ i ] = arrayToCopy.ptr[ i ]; // copy into object
} // end Array copy constructor
 
// destructor for class Array
Array::~Array()
{
   delete [] ptr; // release pointer-based array space
} // end destructor
 
// return number of elements of Array
int Array::getSize() const
{
   return size; // number of elements in Array
} // end function getSize
 
// overloaded assignment operator;
// const return avoids: ( a1 = a2 ) = a3
const Array &Array::operator=( const Array &right )
{
   if ( &right != this ) // avoid self-assignment
   {
      // for Arrays of different sizes, deallocate original
      // left-side array, then allocate new left-side array
      if ( size != right.size )
      {
         delete [] ptr; // release space
         size = right.size; // resize this object
         ptr = new int[ size ]; // create space for array copy
      } // end inner if
 
      for ( int i = 0; i < size; i++ )
         ptr[ i ] = right.ptr[ i ]; // copy array into object
   } // end outer if
 
   return *this; // enables x = y = z, for example
} // end function operator=
 
// determine if two Arrays are equal and
// return true, otherwise return false
bool Array::operator==( const Array &right ) const
{
   if ( size != right.size )
      return false; // arrays of different number of elements
 
   for ( int i = 0; i < size; i++ )
      if ( ptr[ i ] != right.ptr[ i ] )
         return false; // Array contents are not equal
 
   return true; // Arrays are equal
} // end function operator==
 
 
// overloaded subscript operator for non-const Arrays;
// reference return creates a modifiable lvalue
int &Array::operator[]( int subscript )
{
   // check for subscript out-of-range error
   if ( subscript < 0 || subscript >= size )
   {
      cerr << "\nError: Subscript " << subscript 
         << " out of range" << endl;
      exit( 1 ); // terminate program; subscript out of range
   } // end if
 
   return ptr[ subscript ]; // reference return
} // end function operator[]
 
// overloaded subscript operator for const Arrays
// const reference return creates an rvalue
int Array::operator[]( int subscript ) const
{
   // check for subscript out-of-range error
   if ( subscript < 0 || subscript >= size )
   {
      cerr << "\nError: Subscript " << subscript 
         << " out of range" << endl;
      exit( 1 ); // terminate program; subscript out of range
   } // end if
 
   return ptr[ subscript ]; // returns copy of this element
} // end function operator[]
 
// overloaded input operator for class Array;
// inputs values for entire Array
istream &operator>>( istream &input, Array &a )
{
   for ( int i = 0; i < a.size; i++ )
      input >> a.ptr[ i ];
 
   return input; // enables cin >> x >> y;
} // end function 
 
// overloaded output operator for class Array 
ostream &operator<<( ostream &output, const Array &a )
{
   int i;
 
   // output private ptr-based array 
   for ( i = 0; i < a.size; i++ )
   {
      output << setw( 12 ) << a.ptr[ i ];
 
      if ( ( i + 1 ) % 4 == 0 ) // 4 numbers per row of output
         output << endl;
   } // end for
 
   if ( i % 4 != 0 ) // end last line of output
      output << endl;
 
   return output; // enables cout << x << y;
} // end function operator<<
 
/**************************************************************************
 * (C) Copyright 1992-2005 by Deitel & Associates, Inc. and               *
 * Pearson Education, Inc. All Rights Reserved.                           *
 *                                                                        *
 * DISCLAIMER: The authors and publisher of this book have used their     *
 * best efforts in preparing the book. These efforts include the          *
 * development, research, and testing of the theories and programs        *
 * to determine their effectiveness. The authors and publisher make       *
 * no warranty of any kind, expressed or implied, with regard to these    *
 * programs or to the documentation contained in these books. The authors *
 * and publisher shall not be liable in any event for incidental or       *
 * consequential damages in connection with, or arising out of, the       *
 * furnishing, performance, or use of these programs.                     *
 **************************************************************************/
 
 
 
// Fig. 11.8: fig11_08.cpp
// Array class test program.
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
 
#include "Array.h"
 
int main()
{
   Array integers1( 7 ); // seven-element Array
   Array integers2; // 10-element Array by default
 
   // print integers1 size and contents
   cout << "Size of Array integers1 is " 
      << integers1.getSize()
      << "\nArray after initialization:\n" << integers1;
 
   // print integers2 size and contents
   cout << "\nSize of Array integers2 is " 
      << integers2.getSize()
      << "\nArray after initialization:\n" << integers2;
 
   // input and print integers1 and integers2
   cout << "\nEnter 17 integers:" << endl;
   cin >> integers1 >> integers2;
 
   cout << "\nAfter input, the Arrays contain:\n"
      << "integers1:\n" << integers1
      << "integers2:\n" << integers2;
 
   // use overloaded inequality (!=) operator
   cout << "\nEvaluating: integers1 != integers2" << endl;
 
   if ( integers1 != integers2 )
      cout << "integers1 and integers2 are not equal" << endl;
 
   // create Array integers3 using integers1 as an
   // initializer; print size and contents
   
   
   
   
   
   Array integers3( integers1 ); // invokes copy constructor
 
   cout << "\nSize of Array integers3 is "
      << integers3.getSize()
      << "\nArray after initialization:\n" << integers3;
 
   // use overloaded assignment (=) operator
   cout << "\nAssigning integers2 to integers1:" << endl;
   integers1 = integers2; // note target Array is smaller
 
   cout << "integers1:\n" << integers1
      << "integers2:\n" << integers2;
 
   // use overloaded equality (==) operator
   cout << "\nEvaluating: integers1 == integers2" << endl;
 
   if ( integers1 == integers2 )
      cout << "integers1 and integers2 are equal" << endl;
 
   // use overloaded subscript operator to create rvalue
   cout << "\nintegers1[5] is " << integers1[ 5 ];
 
   // use overloaded subscript operator to create lvalue
   cout << "\n\nAssigning 1000 to integers1[5]" << endl;
   integers1[ 5 ] = 1000;
   cout << "integers1:\n" << integers1;
 
   // attempt to use out-of-range subscript
   cout << "\nAttempt to assign 1000 to integers1[15]" << endl;
   integers1[ 15 ] = 1000; // ERROR: out of range
   return 0;
} // end main
 
/**************************************************************************
 * (C) Copyright 1992-2005 by Deitel & Associates, Inc. and               *
 * Pearson Education, Inc. All Rights Reserved.                           *
 *                                                                        *
 * DISCLAIMER: The authors and publisher of this book have used their     *
 * best efforts in preparing the book. These efforts include the          *
 * development, research, and testing of the theories and programs        *
 * to determine their effectiveness. The authors and publisher make       *
 * no warranty of any kind, expressed or implied, with regard to these    *
 * programs or to the documentation contained in these books. The authors *
 * and publisher shall not be liable in any event for incidental or       *
 * consequential damages in connection with, or arising out of, the       *
 * furnishing, performance, or use of these programs.                     *
 **************************************************************************/

Open in new window

0
aditya86b
Asked:
aditya86b
  • 6
  • 3
  • 2
  • +1
3 Solutions
 
evilrixSenior Software Engineer (Avast)Commented:
>> How can I add three more additional operators for the following program.
What operators? Added to what?

See if these links help.

Operator overloading  
http://www.parashift.com/c++-faq-lite/operator-overloading.html

C++ Operator Overloading Guidelines
http://www.cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html
0
 
aditya86bAuthor Commented:
suppose addition operator or subtraction
0
 
evilrixSenior Software Engineer (Avast)Commented:
>> suppose addition operator or subtraction
Do you want them as free standard operators or class member operators? See an example, below, of how to implement these as class member operators. Note that you must return a copy of a new object because the standard + and - operators shouldn't modify the original objects and because of this the member functions should be declared const.

BTW: I don't agree with this...

// overloaded assignment operator;
// const return avoids: ( a1 = a2 ) = a3
const Array &Array::operator=( const Array &right )

It is perfectly acceptable semantics in C/C++ to be able to do chained assignments
x = y = z; // This is a perfectly valid bit of code.

#include <cassert>
 
class foo
{
public:
 
	foo(int n = 0) : n_(n){}
 
	foo operator + (int n) const
	{
		return foo(n_ + n);
	}
 
	foo operator + (foo const & f) const
	{
		return *this + f.n_;
	}
 
	foo operator - (int n) const
	{
		return foo(n_ - n);
	}
 
	foo operator - (foo const & f) const
	{
		return *this - f.n_;
	}
 
	int GetInt() const { return n_; }
 
private:
	int n_;
};
 
 
int main()
{
	foo f1(1);
	foo f2(2);
 
	foo f3 = f1 + f2;
	foo f4 = f3 - f2 + 3;
 
	assert(f1.GetInt() == 1);
	assert(f2.GetInt() == 2);
	assert(f3.GetInt() == 3);
	assert(f4.GetInt() == 4);
}

Open in new window

0
Efficient way to get backups off site to Azure

This user guide provides instructions on how to deploy and configure both a StoneFly Scale Out NAS Enterprise Cloud Drive virtual machine and Veeam Cloud Connect in the Microsoft Azure Cloud.

 
rstaveleyCommented:
Hi evilrix,

> I don't agree with this...

You overlooked the parentheses in the comment. The const return reference is to avoid the double-assignment illustrated in the code snippet in object-4.

Chains work fine with const return references.

#include <iostream>
#include <string>
using namespace std;
 
class object;
ostream& operator<<(ostream&,const object&);
 
class object {
	string name;
	friend ostream& operator<<(ostream&,const object&);
	int instance;
	static int instances;
public:
	object(const string& name) : name(name),instance(++instances) {
	}
	/*const*/ object& operator=(const object& o) {
		name = o.name;
		return *this;
	}
};
 
int object::instances = 0;
 
ostream& operator<<(ostream& os,const object& o) {
	return os << "object-" << o.instance << "-" << o.name;
}
 
int main()
{
	{ 
		object o1 = "tom";
		object o2 = "dick";
		object o3 = "sally";
		cout << "Start with " << o1 << ", " << o2 << " and " << o3 << endl;
		o1 = o2 = o3; // Works with const
		cout << "Wind up with with " << o1 << ", " << o2 << " and " << o3 << endl;
	}
	{
		object o1 = "tom";
		object o2 = "dick";
		object o3 = "sally";
		cout << "Start with " << o1 << ", " << o2 << " and " << o3 << endl;
		(o1 = o2) = o3; // Doesn't work with const
		cout << "Wind up with with " << o1 << ", " << o2 << " and " << o3 << endl;
	}
}

Open in new window

0
 
evilrixSenior Software Engineer (Avast)Commented:
>> Chains work fine with const return references.
Yes, you're right (my head was in the clouds there); however, I still don't agree with it for the following reason...

Firstly, let's take the implementation of the std::vector's assignment operator as an example...

_Myt& operator=(const _Myt& _Right); // Does not return const reference

Secondly, it means the assignment semantics will not be the same as the standard built-in or synthesised assignment operators. This can impact ones ability to write generic code as the behaviour would be unexpected.

template <typename T>
void bar(T & t)
{
}
 
template <typename T>
void foo(T & t1, T & t2, T & t3)
{
	(t1 = t2) = t3;
	bar(t3 = t1);
}
 
struct ArrayBad
{
	const ArrayBad & operator=( const ArrayBad &right )
	{
		return *this;
	}
};
 
struct ArrayGood
{
	ArrayGood &operator=( const ArrayGood &right )
	{
		return *this;
	}
};
 
struct Default{};
 
int main()
{
	int i1, i2, i3;
	foo(i1, i2, i3);
 
	Default d1, d2, d3;
	foo(d1, d2, d3);
 
	ArrayGood ag1, ag2, ag3;
	foo(ag1, ag2, ag3);
 
	// This class has wrong assignment semantics, it doesn't behave like built-in types
	// not does the assignment operator behave like a compiler synthersised operator
	ArrayBad ab1, ab2, ab3;
	foo(ab1, ab2, ab3);
}

Open in new window

0
 
itsmeandnobodyelseCommented:
>> suppose addition operator or subtraction

If addition is defined as creating a new array which includes all elements of both input arrays, it is

Array Array::operator+(const Array & right)
{
    int i = 0;
    int n = 0;
    Array arr(getSize()+right.getSize());   // make correct size
    for (i = 0; i < getSize(); ++i)
         arr[i] = (*this)[i];
    for (n = 0; n < right.getSize(); ++n,++i)
         arr[i] = right[n];
    return arr;
}

Note, you didn't post the array.h header. So, I didn't know whether you have an insert or append (push_back) member function.

If addition is defined as creating a new array which includes all unique elements of both input arrays, it is

Array Array::operator+(const Array & right)
{
    int i = 0;
    int n = 0;
    int k = 0;
   // make a temporary copy same size as left array
    Array temp(getSize());  
    bool dupl =false;
    // assume both input arrays have no duplicate entries themselves
    for (i = 0; i < getSize(); ++i)
    {
       dupl = false;
       for (n = 0; n < right.getSize(); ++n)
       {
            if ((*this)[i] == right[n])
            {
                 dupl = true;
                 break;
            }
       }
        if (dupl) continue;  // skip duplicate in first array
        temp[k] = (*this)[i];
        k++;
    }
    // Now we know how many elements were in the 'sum' array
    Array arr(k + right.getSize());  

    for (i = 0; i < k; ++i)
         arr[i] = temp[i];
    for (n = 0; n < right.getSize(); ++n,++k)
         arr[k] = right[n];
    return arr;
}

A further way to implement operator+  (for both the above definitions of addition) is to declare a friend global operator+ which takes two arguments:

class Array
{
    ...
  public:
    friend Array operator+(const Array & left, const Array & right);
   ...  
};

You can implement it in Array.cpp though it is not a member of Array class but a global function:

// array.cpp
...
Array operator+(const Array & left, const Array & right)
{
    ....
}

The implementation is nearly identical to one of the above samples beside that (*this) must be replaced by left and all calls of getSize by left.getSize.

The subtraction only makes sense if you define substraction as to remove entries from the left array which are also in the right array. You could implement it similar to the second sample for operator+ by using a temporary array. In case you have a remove member function you can spare the temporary array but only make a copy of the left array and remove the entries which were in the right array as well.


>>>> const Array &Array::operator=( const Array &right )

I agree with evilrix that the return value should be non-const. As it is a temporary array only which only lives within the current scope, it actually makes less sense to make it const. You only would force the caller to make an (unnecessary) copy if he/she wants to work further with the array:

   Array & a1 =  a2 + a3;     // wouldn't work if const Array return
   (a1 + a2).append(0);       // wouldn't work either
   Array a = (a2 + a3) + a4; // wouldn't work either

No, returning a const here isn't well-defined and an annoyance.
0
 
evilrixSenior Software Engineer (Avast)Commented:
>> the return value should be non-const. As it is a temporary array
What should be returned is a non-const reference to *this
0
 
itsmeandnobodyelseCommented:
>>>> What should be returned is a non-const reference to *this
No. The operator+ returns a new Array by Value.  The operator+= would return a non-const reference to this ...
0
 
evilrixSenior Software Engineer (Avast)Commented:
>> No. The operator+ returns a new Array by Value.

Yes as it should and as I have already pointed out above where I state, "Note that you must return a copy of a new object because the standard + and - operators shouldn't modify the original objects".

However, I was refering to this comment you made...

">>>> const Array &Array::operator=( const Array &right )
I agree with evilrix that the return value should be non-const. As it is a temporary array only which only lives within the current scope"

Clearly, and operator=() should not return a temporary, it returns a reference to *this and, as we both agree, it should be non-const.

I think we are just talking at cross purposes, but I don't want to confuse aditya86b. :)
0
 
rstaveleyCommented:
> however, I still don't agree with it for the following reason...

I agree with you, evilrix, for an Array class. I expect if Dietel had another 13 years to work on that code, they might change their position too ;-)
0
 
itsmeandnobodyelseCommented:
>>>> I agree with evilrix that the return value should be non-const.

Sorry, you are right. I was mixing up operator= and operator+ (and possibly have confused the asker).

It isn't quite easy to find an example where the const return of operator= does any harm though ...

  (a.operator=(a1)).append(12345);

wouldn't compile but I don't know whether that is a statement of relevance ...

  (a = a1).append(98765);

compiles if a is a non-const Array.
0
 
evilrixSenior Software Engineer (Avast)Commented:
>> It isn't quite easy to find an example where the const return of operator= does any harm though
It's not so much harm as semantically incorrect in terms of it doesn't fit with the standard nor compiler synthesised operators and, as I point out, this could be problematic when writing generic code. That said, the problem it tries to address is reasonable; however, (a) is it really a problem? and (b) even if it is since you can still do it with integral and compiler synthesized types changing your own types to behave differently is just going to confuse.

So to sum up, I applaud the idea (I think, although I'd have to check, Scott Meyer advocates doing this, or something like it); however, personally I prefer my semantics consistent even if they aren't wholly correct :)
0

Featured Post

Important Lessons on Recovering from Petya

In their most recent webinar, Skyport Systems explores ways to isolate and protect critical databases to keep the core of your company safe from harm.

  • 6
  • 3
  • 2
  • +1
Tackle projects and never again get stuck behind a technical roadblock.
Join Now