280 likes | 402 Views
ECE230 Lectures Series. Operator Overloading. Ying Wu Electrical & Computer Engineering Northwestern University yingwu@ece.northwestern.edu. Let’s program. Create a CMatrix class Data members? Get inputs? Print it out? Arithmetic operations?. 1 st version. class CMatrix {
E N D
ECE230 Lectures Series Operator Overloading Ying Wu Electrical & Computer Engineering Northwestern University yingwu@ece.northwestern.edu
Let’s program • Create a CMatrix class • Data members? • Get inputs? • Print it out? • Arithmetic operations?
1st version class CMatrix { double *m_pData; int m_nNRow; int m_nNCol; public: CMatrix(int nrow = 1, int ncol = 1); // default constructor CMatrix(const CMatrix& m); // copy constructor ~CMatrix(); // destructor // getting and setting int NRow() const { return m_nNRow; }; int NCol() const { return m_nNCol; }; int Size() const { return m_nNRow*m_nNCol; }; // assignment const CMatrix& operator=(const CMatrix& m); const CMatrix& operator=(const double& k); };
CMatrix::CMatrix(int nrow, int ncol) { m_nNRow = nrow; m_nNCol = ncol; int size = m_nNRow*m_nNCol; m_pData = new double [size]; for(int i=0;i<size;i++) m_pData[i] = 0; } // copy constructor CMatrix::CMatrix(const CMatrix& m) : m_nNRow(m.m_nNRow), m_nNCol(m.m_nNCol) { int size = m_nNRow*m_nNCol; m_pData = new double [size]; for(int i=0;i<size;i++) m_pData[i] = m.m_pData[i]; } CMatrix::~CMatrix() { delete [] m_pData; }
const CMatrix& CMatrix::operator=(const CMatrix& m) { int size = m_nNRow*m_nNCol; if(&m != this){ // check for self-assignment if(m_nNRow!=m.m_nNRow || m_nNCol!=m.m_nNCol){ delete [] m_pData; m_nNRow = m.m_nNRow; m_nNCol = m.m_nNCol; size = m_nNRow*m_nNCol; m_pData = new double [size]; } for(int i=0;i<size;i++) m_pData[i] = m.m_pData[i]; } return *this; } const CMatrix& CMatrix::operator=(const double& k) { if(m_nNRow!=1 || m_nNCol!=1){ delete [] m_pData; m_nNRow = 1; m_nNCol = 1; m_pData = new double; } m_pData[0] = k; return *this; }
Use it! void main() { CMatrix a(3,4); cout << a.NRow() << a.NCol() << endl; CMatrix ttt = 10; // what does it mean? Make sure you understand it. CMatrix b; b = a; b = 2; // what does it mean? CMatrix c; c = 5; }
What to add? • Overloading operators • Indexing? • Stream I/O? • Unary operations? • ++, -- • Binary operations? • == • != • +, -, *, • +=, -=, *=
Operator overloading • Operator overloading • Enabling C++’s operators to work with class objects • Using traditional operators with user-defined objects • Requires great care; when overloading is misused, program difficult to understand • Examples of already overloaded operators • Operator << is both the stream-insertion operator and the bitwise left-shift operator • + and -, perform arithmetic on multiple types • Compiler generates the appropriate code based on the manner in which the operator is used
Fundamentals • Overloading an operator • Write function definition as normal • Function name is keyword operator followed by the symbol for the operator being overloaded • operator+ used to overload the addition operator (+) • Using operators • To use an operator on a class object, it must be overloaded except the assignment operator(=)or the address operator(&) • Assignment operator by default performs memberwise assignment • Address operator (&) by default returns the address of an object
Question? • I want a natural way to output a matrix i.e., CMatrix a(3,4); cout << a; • How can I do that?
Overloading Stream-Insertion and Stream-Extraction Operators • Overloaded << and >> operators • Overloaded to perform input/output for user-defined types • Left operand of types ostream& and istream& • Must be a non-member function because left operand is not an object of the class • Must be a friend function to access private data members
2nd version: friend stream I/O class CMatrix { double *m_pData; int m_nNRow; int m_nNCol; public: friend ostream &operator<<( ostream &, const CMatrix &); CMatrix(int nrow = 1, int ncol = 1); // default constructor CMatrix(const CMatrix& m); // copy constructor ~CMatrix(); // destructor // getting and setting int NRow() const { return m_nNRow; }; int NCol() const { return m_nNCol; }; int Size() const { return m_nNRow*m_nNCol; }; // assignment const CMatrix& operator=(const CMatrix& m); const CMatrix& operator=(const double& k); };
// friend ostream ostream &operator<<( ostream &output, const CMatrix &m) { output.setf(ios::left, ios::adjustfield); output.precision(3); output << endl << "\t\t"; for(int r=0;r<m.m_nNRow;r++){ for(int c=0;c<m.m_nNCol;c++){ output << setw(8) << m(r,c); } if(r!=m.m_nNRow-1) output << endl << "\t\t"; } return output; }
Operator Functions as Class Members vs. as friend Functions • Member vs non-member • Operator functions can be member or non-member functions • When overloading (), [], -> or any of the assignment operators, must use a member function • Operator functions as member functions • Leftmost operand must be an object (or reference to an object) of the class • If left operand of a different type, operator function must be a non-member function • Operator functions as non-member functions • Must be friends if needs to access private or protected members • Enable the operator to be commutative
I want more … • I want to index the elements in a matrix i.e., CMatrix a(3,4); double t; t = a(1,1) + a(2,1); • How can I do that?
3rd version: indexing class CMatrix { double *m_pData; int m_nNRow; int m_nNCol; public: friend ostream &operator<<( ostream &, const CMatrix &); CMatrix(int nrow = 1, int ncol = 1); // default constructor CMatrix(const CMatrix& m); // copy constructor ~CMatrix(); // destructor // getting and setting int NRow() const { return m_nNRow; }; int NCol() const { return m_nNCol; }; int Size() const { return m_nNRow*m_nNCol; }; // assignment const CMatrix& operator=(const CMatrix& m); const CMatrix& operator=(const double& k); // indexing double& operator()(int, int); const double& operator()(int, int) const; };
// overloading indexing // reference return creates a lvalue (left value) double& CMatrix::operator()(int r, int c) { assert(r>=0 && r<m_nNRow); assert(c>=0 && c<m_nNCol); return m_pData[r*m_nNCol+c]; } // const reference return creates a rvalue (right value) const double& CMatrix::operator()(int r, int c) const { assert(r>=0 && r<m_nNRow); assert(c>=0 && c<m_nNCol); return m_pData[r*m_nNCol+c]; }
I want to compare two matrices. • Compare two matrices? CMatrix a(3,4) CMatrix b(3,4) if( a==b){ cout << “they are equal” << endl; } else cout << “they are not equal” << endl; • How can I do that?
4th version: == and != class CMatrix { double *m_pData; int m_nNRow; int m_nNCol; public: friend ostream &operator<<( ostream &, const CMatrix &); CMatrix(int nrow = 1, int ncol = 1); // default constructor CMatrix(const CMatrix& m); // copy constructor ~CMatrix(); // destructor int NRow() const { return m_nNRow; }; int NCol() const { return m_nNCol; }; int Size() const { return m_nNRow*m_nNCol; }; const CMatrix& operator=(const CMatrix& m); const CMatrix& operator=(const double& k); double& operator()(int, int); const double& operator()(int, int) const; // compare equal bool operator==(const CMatrix& m) const; bool operator==(const double& v) const; // not equal bool operator!=(const CMatrix &m) const { return !(*this==m); }; bool operator!=(const double& v) const { return !(*this==v); }; };
bool CMatrix::operator==(const CMatrix &m) const { if(m_nNRow!=m.m_nNRow || m_nNCol!=m.m_nNCol) return false; int size = m_nNRow*m_nNCol; for(int i=0;i<size;i++){ if( m_pData[i] != m.m_pData[i]) return false; } return true; } bool CMatrix::operator==(const double& v) const { for(int i=0; i<Size(); i++){ if(m_pData[i] != v) return false; } return true; }
I want a simple addition! • Wanna use CMatrix like a regular variable? CMatrix a(3,4); CMatrix b(3,4); CMatrix c(3,4); c = a+b; c = a+1; • How can I do that?
5th version: + class CMatrix { double *m_pData; int m_nNRow; int m_nNCol; public: friend ostream &operator<<( ostream &, const CMatrix &); CMatrix(int nrow = 1, int ncol = 1); // default constructor CMatrix(const CMatrix& m); // copy constructor ~CMatrix(); // destructor int NRow() const { return m_nNRow; }; int NCol() const { return m_nNCol; }; int Size() const { return m_nNRow*m_nNCol; }; const CMatrix& operator=(const CMatrix& m); const CMatrix& operator=(const double& k); double& operator()(int, int); const double& operator()(int, int) const; bool operator==(const CMatrix& m) const; bool operator==(const double& v) const; bool operator!=(const CMatrix &m) const { return !(*this==m); }; bool operator!=(const double& v) const { return !(*this==v); }; CMatrix operator+(const CMatrix& m); CMatrix operator+(const double& t); };
// overloading + CMatrix CMatrix::operator+(const CMatrix& m) { assert(m_nNRow == m.m_nNRow && m_nNCol == m.m_nNCol); CMatrix tmp(m_nNRow,m_nNCol); int size = m_nNRow*m_nNCol; for(int i=0;i<size;i++) tmp.m_pData[i] = m_pData[i] + m.m_pData[i]; return tmp; } CMatrix CMatrix::operator+(const double& t) { CMatrix tmp(m_nNRow,m_nNCol); int size = m_nNRow*m_nNCol; for(int i=0;i<size;i++) tmp.m_pData[i] = m_pData[i] + t; return tmp; }
I want +=? • A step further? CMatrix a(3,4); CMatrix b(3,4); a += b; a += 2; • How can I do that?
6th version: += class CMatrix { double *m_pData; int m_nNRow, m_nNCol; public: friend ostream &operator<<( ostream &, const CMatrix &); CMatrix(int nrow = 1, int ncol = 1); // default constructor CMatrix(const CMatrix& m); // copy constructor ~CMatrix(); // destructor int NRow() const { return m_nNRow; }; int NCol() const { return m_nNCol; }; int Size() const { return m_nNRow*m_nNCol; }; const CMatrix& operator=(const CMatrix& m); const CMatrix& operator=(const double& k); double& operator()(int, int); const double& operator()(int, int) const; bool operator==(const CMatrix& m) const; bool operator==(const double& v) const; bool operator!=(const CMatrix &m) const { return !(*this==m); }; bool operator!=(const double& v) const { return !(*this==v); }; CMatrix operator+(const CMatrix& m); CMatrix operator+(const double& t); CMatrix& operator+=(const CMatrix& m); CMatrix& operator+=(const double& t); };
// overloading += CMatrix& CMatrix::operator+=(const CMatrix& m) { assert(m_nNRow == m.m_nNRow && m_nNCol == m.m_nNCol); int size = m_nNRow*m_nNCol; for(int i=0;i<size;i++) m_pData[i] += m.m_pData[i]; return *this; } CMatrix& CMatrix::operator+=(const double& t) { int size = m_nNRow*m_nNCol; for(int i=0;i<size;i++) m_pData[i] += t; return *this; }
Restrictions • C++ operators that can be overloaded • C++ Operators that cannot be overloaded
Restrictions • Overloading restrictions • Precedence of an operator cannot be changed • Associativity of an operator cannot be changed • Arity (number of operands) cannot be changed • Unary operators remain unary, and binary operators remain binary • Operators &, *, + and - each have unary and binary versions • Unary and binary versions can be overloaded separately • No new operators can be created • Use only existing operators • No overloading operators for built-in types • Cannot change how two integers are added • Produces a syntax error