Member function that overloads the assignment operator
Overloading C++ Assignment Operator =
Write a member function that overloads the assignment operator for the matrix class so that the operator assigns one two-dimensional array to another. Before looking at overloading the assignment operator, you may want to review the use of
Reference Declaration in C++
C++ allows reference declarations so you can create aliases for variables.
These declarations are typically of the form: type&identifier=variable. Reference declarations declare the identifier to be an alternative name, or alias, for a variable specified in an initialization of the reference. In effect, reference declarations produce lvalues:
On the right side of an assignment expression, an lvalue is automatically dereferenced.
On the left side, it specifies where an appropriate value is to be stored.
Passing and returning values from functions can be expensive, especially when these values are large aggregates and require copying.
Using reference declarations can avoid this overhead. Multiple assignment expressions ordinarily require the creation of temporaries to perform them. By using reference return types, this can be managed efficiently. For ADTs, we must define such expressions unless satisfactory defaults are available.
C++ Reference: It can be easier to understand complicated pointer or reference declarations if you read them from right to left. A reference is a synonym for an object or function. A reference is declared just like a pointer, but with an ampersand (&) instead of an asterisk (*). A 1) local or 2) global reference declaration must have an initializer that specifies the target of the reference. Data members and function parameters, however, do not have initializers. You cannot declare
a reference of a reference,
a reference to a class member,
a pointer to a reference,
an array of references, or
a cv-qualified reference.
For example:
int x;
int &r = x; // Reference to int
int& const rc = x; // Error: no cv qualified references
int & &rr; // Error: no reference of reference
int& ra[10]; // Error: no arrays of reference
int &* rp = &r; // Error: no pointer to reference
int* p = &x; // Pointer to int
int *& pr = p; // OK: reference to pointer
A reference, unlike a pointer, cannot be made to refer to a different object at runtime. Assignments to a reference are just like assignments to the referenced object. Because a reference cannot have cv-qualifiers, there is no such thing as a const reference. Instead, a reference to const is often called a const reference for the sake of brevity. References are often used to bind names to temporary objects, implement call-byreference for function parameters, and optimize call-by-value for large function parameters. The divide function in Figure 1 demonstrates the first two uses. The standard library has the div function, which divides two integers and returns the quotienttient and remainder in a struct. Instead of copying the structure to a local object, divide binds the return value to a reference to const,
thereby avoiding an unnecessary copy of the return value. Furthermore, suppose that you would rather have divide return the results as arguments. The function parameters quotient and rem are references; when the divide function is called, they are bound to the function arguments, q and r, in main. When divide assigns to quotient, it actually stores the value in q, so when divide returns, main has the quotient and remainder.
Figure 1. Returning results in function arguments
#include <cstdlib>
#include <iostream>
#include <ostream>
void divide(long num, long den, long& quotient, long& rem){
const std::ldiv_t& result = std::div(num, den);
quotient = result.quotient;
rem = result.rem;
}
int main( ){
long q, r;
divide(42, 5, q, r);
std::cout << q << " remainder " << r << '\n';
}
Binary Operator Overloading
Look at the binary operator overloading by focusing on the safe array class from the Building Classes in C++ course. It would be convenient to assign one array to another. We can specify the behavior of assignment by overloading the assignment operator (=). It is good style to maintain consistency with standard usage and avoid creating personal algebras.
Safe Array in C++: Here is the implementation of a safe array that was used in Building Classes in C++:
//Implementation of a safe array type vect
class vect {
public:
explicit vect(int n = 10);
~vect() { delete []p; }
int& element(int i); //access p[i]
int ub() const
{ return (size - 1); } //upper bound
private:
int* p;
int size;
};
vect::vect(int n) : size(n){
assert(n > 0);
p = new int[size];
assert(p);
}
int& vect::element(int i){
assert (i >= 0 && i < size);
return p[i];
}
Following is the code to create a safearray in C++.
#include<oaidl.h>
void CreateSafeArray(SAFEARRAY* saData){
double data[10]; // some sample data to write into the created safearray
SAFEARRAYBOUND Bound;
Bound.lLbound = 0;
Bound.cElements = 10;
*saData = SafeArrayCreate(VT_R8, 1, &Bound);
double HUGEP *pdFreq;
HRESULT hr = SafeArrayAccessData(*saData, (void HUGEP* FAR*)&pdFreq);
if (SUCCEEDED(hr)) {
// copy sample values from data[] to this safearray
for (DWORD i = 0; i < 10; i++){
*pdFreq++ = data[i];
}
SafeArrayUnaccessData(*saData);
}
}
When you are finished free the pointer using the following code.
SAFEARRAY* saData;
CreateSafeArray(&saData); // Create the safe array
// use the safearray
...
...
// Call the SafeArrayDestroy to destroy the safearray
SafeArrayDestroy(saData);
saData = NULL; // set the pointer to NULL
We can add the following member function, which overloads the assignment operator for the vect class:
Operator Overloading Guidelines in C++
Any time a class uses new to construct objects, it should provide an explicitly overloaded assignment operator. This advice is analogous to our rule that such a class provide an explicit copy constructor. The compiler-provided default assignment operator semantics would, in most cases, give spurious behavior. This leads to a suggested normal form for classes with heap-managed memory. This normal form rule applies as well to reference counted classes. The reason the assignment operator returns a reference is to allow assignment to work efficiently. This requires lvalue semantics.
Give special meanings to Operators
In C++ you can give special meanings to operators, when they are used with user-defined classes. This is called operator overloading. You can implement C++ operator overloads by providing special member-functions on your classes that follow a particular naming convention. For example, to overload the + operator for your class, you would provide a member-function named operator+ on your class. The following set of operators is commonly overloaded for user-defined classes:
= (assignment operator)
+ - * (binary arithmetic operators)
+= -= *= (compound assignment operators)
== != (comparison operators)
Overloading the assignment operator and Program dissection
In the following diagram the 1) operator= 2) assert and 3) for loop to to read a description of their purpose.
vect& vect::operator=(const vect& v)
{
assert(v.size == size);
for (int i = 0; i < size; ++i)
p[i] = v.p[i];
return (*this);
}
The "operator=()" function returns reference to "vect" and has one explicit argument of type reference to "vect". The first argument of the assignment operator is the implicit argument. The function could have been written to return void, but then it would not have allowed multiple assignment.
This guarantees that the sizes are compatible.
The explicit argument "v.p[]" will be the right side of the assignment. The implicit argument "p[]" will be the left side of the assignment. The self-referential pointer is dereferenced and passed back as the value of the expression. This allows multiple assignment with right-to-left associativity to be defined.
operator=()
vect& vect::operator=(const vect& v)
The operator=() function returns reference to vect and has one explicit argument of type reference to vect. The first argument of the assignment operator is the implicit argument. The function could have been written to return void, but then it would not have allowed multiple assignment.
assert(v.size == size);
This guarantees that the sizes are compatible.
for (int i = 0; i < size; ++i)
p[i] = v.p[i];
return (*this);
The explicit argument v.p[] will be the right side of the assignment; the implicit argument p[] will be the left side of the assignment. The self-referential pointer is dereferenced and passed back as the value of the expression.
This allows multiple assignment with right-to-left associativity to be defined.
Now with the class vect, the following expressions are meaningful:
a = b; //a, b are type vect
a = b= c; //a, b, c are type vect
a = vect(data, DSIZE); //convert array data[DSIZE]
Overloading Assignment Exercise
Click the Exercise link below to write a member function that overloads the assignment operator for the matrix class so that the operator assigns one two-dimensional array to another. Overloading Assignment Exercise