Presentation is loading. Please wait.

Presentation is loading. Please wait.

Matrices Introducing Inheritance. Consider A matrix is a grid in which numbers can be stored. Algorithms for problems in scientific computing frequently.

Similar presentations


Presentation on theme: "Matrices Introducing Inheritance. Consider A matrix is a grid in which numbers can be stored. Algorithms for problems in scientific computing frequently."— Presentation transcript:

1 Matrices Introducing Inheritance

2 Consider A matrix is a grid in which numbers can be stored. Algorithms for problems in scientific computing frequently store the coefficients for a system of equations in a matrix, for convenient manipulation. A class to represent matrices is thus a useful class for scientific computing. 192 837 465

3 Problem Let’s write a program to perform matrix addition. 192 837 465 123 456 789 2115 12813 111410 +  m1m2 m3 For simplicity, we will store a matrix’s values in a file. Since there is no predefined Matrix type, we will design a class... Since there is no predefined Matrix type, we will design a Matrix class...

4 Behavior Our program should explain its purpose, and prompt for and read the values of the first matrix from the keyboard. It should then do the same for the second matrix. It should then add the two matrices producing a third matrix. It should then display the third matrix to the screen.

5 Objects Description Type Kind Name 1st matrix Matrix varying matrix1 3rd matrix Matrix varying matrix3 screen ostream varying cout 2cd matrix Matrix varying matrix2 keyboard istream varying cin

6 Operations Description Predefined? Library? Name display a string yes string << read a matrix no --- >> from istream add 2 matrices no --- ??? write a matrix no --- ??? to ostream

7 Algorithm 0. Display purpose of program 1. Prompt for and read matrix1 using cin. 2. Prompt for and read matrix2 using cin. 3. Compute matrix3 = matrix1 + matrix2. 4. Write matrix3 to cout.

8 Coding Our Algorithm Ideally, we want to implement our algorithm this way: // matAdd.cpp //... documentation //... other #includes #include “Matrix.h” int main() { cout << “\nTo add two matrices, enter the” << “\n values of the first matrix“ << “\n (one row per line)\n”; Matrix mat1; cin >> mat1; cout << “\nDo the same for the second matrix:\n”; Matrix mat2; cin >> mat2; //...

9 Coding (Ct’d) //... matAdd.cpp continued Matrix mat3 = mat1 + mat2; cout << “\nThe sum of\n” << mat1 << “\n\n and\n\n” << mat2 << “\n\n is\n\n” << mat3 << “\n\n”; } If we build an easy-to-use Matrix class, writing a program to manipulate matrices can be as easy as writing a program that manipulates integers.

10 Analysis We have a few function members to define! Our algorithms requires: –matrix input from an istream –matrix addition –matrix output to an ostream In addition, we should provide “normal” class operations (normal constructors, accessors) as well as other Matrix operations.

11 Difficulty We could declare our Matrix class as follows: class Matrix { public: //... private: int myRows, myColumns; typedef vector Row; vector myGrid; }; However, if we do so, then we must redefine many of the vector operations for our Matrix class, which will greatly increase our development time.

12 For Example If we take this approach, then we will be unable to use the subscript operator on a Matrix object: Matrix mat1; //... cout << mat1[0][0] << endl; The reason is that a Matrix declared using this approach has-a vector of vectors as a data member, but such Matrix is not a vector of vectors. That is, although subscript is an operation defined for class vector, it is not defined for a Matrix unless we define it (as is the case for all vector operations).

13 A Solution One way to avoid this problem is to declare our Matrix class as an extension to vector of vectors: typedef vector Row; class Matrix : public vector { //... }; This approach tells the compiler that a Matrix is a. This approach tells the compiler that a Matrix is a vector >. Since such a Matrix is a, any operation that can be applied to can be applied to a Matrix (including subscript)! Since such a Matrix is a vector, any operation that can be applied to vector can be applied to a Matrix (including subscript)!

14 Example With this definition (and a constructor we’ll write shortly), we can write: Matrix mat1(2,3); inherited data member(s) [0] [1] [0][1][2] mat1 cin >> mat1[0][0];

15 Example With this definition (and a constructor we’ll write shortly), we can write: Matrix mat1(2,3); inherited data member(s) [0] [1] [0][1][2] mat1 cin >> mat1[0][0]; The first subscript selects the whose index is 0 within mat1. The first subscript selects the Row whose index is 0 within mat1.

16 Example With this definition (and a constructor we’ll write shortly), we can write: Matrix mat1(2,3); inherited data member(s) [0] [1] [0][1][2] mat1 cin >> mat1[0][0]; The first subscript selects the whose index is 0 within mat1. The first subscript selects the Row whose index is 0 within mat1. The second subscript selects the column whose index is 0 with in that. The second subscript selects the column whose index is 0 with in that Row.

17 Inheritance In such a declaration, Matrix is said to be derived from. In such a declaration, Matrix is said to be derived from vector. The Matrix class is said to inherit all of the members (data and function) of. The Matrix class is said to inherit all of the members (data and function) of vector. The pattern for such a declaration is as follows: class ChildClass : public ParentClass { //... }; ChildClass is derived from ParentClass, and inherits all of its members, both function and data.

18 Inheritance (Ct’d) The is-a relationship is often drawn like this: Parent Class Derived Class A parent class can have many derived classes, which are sometimes called child classes: Parent Class Child1Child2ChildN...

19 Inheritance (Ct’d) Child classes can also be parents, producing class hierarchies: Such hierarchies are useful for modeling relationships among real world objects. Parent Class Child1Child2ChildN... GChild1GChild2GChild3 Vehicle BoatCarTruck... compactwagon... sedan... By consolidating common code in parent classes, inheritance can eliminate all redundant code.

20 Using Inheritance Inheritance can be used to declare any class that is a special instance of another class. Since the derived class inherits all members of the parent class, inheritance should only be used if every operation on the parent class can be appropriately applied to the derived class

21 Declaring Matrix We can thus start a Matrix class as follows: typedef vector Row; class Matrix : public vector { public: private: int myRows, myColumns; }; No myGrid data member is needed, because Matrix inherits the implementation details of vector. The data members myRows and myColumns are not required, but they simplify some operations.

22 The Matrix Interface class Matrix : public vector { public: private: //... data members omitted }; Matrix(); Matrix(int rows, int columns); int Rows() const; int Columns() const; Matrix operator+(const Matrix & mat2) const; //... other Matrix-specific operations void Read(istream & in); void Print(ostream & out) const; friend istream & operator>>(istream & in, Matrix & chart); friend ostream & operator<<(ostream & in, const Matrix & chart);

23 Default Constructor The default constructor initializes the data members to default values: Matrix mat1; Specification: Postcondition: myRows == 0 && myColumns == 0. We should use the vector constructor to initialize the inherited data members...

24 Default Constructor inline Matrix::Matrix() : vector () { myRows = 0; myColumns = 0; } This is sufficiently simple to define inline in Student.h:

25 Default Constructor inline Matrix::Matrix() : vector () { myRows = 0; myColumns = 0; } This is sufficiently simple to define inline in Student.h: The notation calls the constructor for class (Matrix’s parent class). The notation : vector () calls the constructor for class vector (Matrix’s parent class). A derived class constructor can (and should) always use this pattern to call the constructor of its parent class, to initialize its inherited data members.

26 Explicit-Value Constructor Matrix mat1(3, 5); This constructor lets you construct a Matrix of a specified size (in rows and columns): Specification: Receive: rows, columns, two int values. Precondition: rows > 0 && columns > 0. Postcondition: myRows == rows && myColumns == columns && I contain a 2-D vector of rows rows and columns columns. 00000 00000 00000 [0][1][2][3][4] [2] [1] [0] ??? mat1 myRows myColumns 3 5 The inherited data are wrapped in orange.

27 Explicit-Value Constructor inline Matrix:: Matrix(int rows, int columns) : vector (rows, Row(columns)) { assert(rows > 0 && columns > 0); myRows = rows; myColumns = columns; } This is sufficiently simple to define inline:

28 Explicit-Value Constructor inline Matrix:: Matrix(int rows, int columns) : vector (rows, Row(columns)) { assert(rows > 0 && columns > 0); myRows = rows; myColumns = columns; } This is sufficiently simple to define inline: The calls to initialize the inherited members. The : vector (rows, Row(columns)) calls vector () to initialize the inherited members. This constructor lets the caller specify the size (), and the initial value () of the vector. This constructor lets the caller specify the size ( rows ), and the initial value ( Row(columns) ) of the vector. The constructor (i.e., ) is used to define the initial value as a vector of size. The Row constructor (i.e., vector ) is used to define the initial value as a vector of size columns.

29 Extractors The extractors retrieve data member values: cout << mat1.Rows() << mat1.Columns(); Specifications: Rows():Return myRows. Rows():Return myRows. Columns():Return myColumns.

30 Extractors inline int Matrix::Rows() const { return myRows; } These are sufficiently simple to define inline: inline int Matrix::Columns() const { return myColumns; }

31 Element Access Thanks to our having derived Matrix from vector, we can write: cout << mat1[r][c]; and access the element at row, column, using the inherited subscript operators. and access the element at row r, column c, using the inherited subscript operators.

32 Element Access Thanks to our having derived Matrix from vector, we can write: cout << mat1[r][c]; and access the element at row r, column c, using the inherited subscript operators. Since mat1 is a vector, sending mat1 the subscript message [r] accesses the Row in mat1 whose index is r.

33 Element Access Thanks to our having derived Matrix from vector, we can write: cout << mat1[r][c]; and access the element at row r, column c, using the inherited subscript operators. Since is a vector, sending the subscript message [r] accesses the Row in whose index is r. Since mat1 is a vector, sending mat1 the subscript message [r] accesses the Row in mat1 whose index is r. We then send that Row the subscript message [c], which accesses the column within that Row whose index is c.

34 Print() mat1.Print(cout); This member lets you write a matrix to an ostream: Specification: Receive: out, an ostream. Output: my (Matrix) values, to out. Passback: out, containing my Matrix values;

35 Defining Print() //... void Matrix::Print(ostream & out) const { for (int r = 0; r < Rows(); r++) // for each r for (int c = 0; c < Columns(); c++) // for each c { fout << (*this)[r][c]; // display if (c < Columns()-1) // either out << ‘\t’; // tab else // or out << ‘\n’; // newline } This is sufficiently complicated to define separately.

36 this The tricky thing here is that within a function member, we must send ourselves the subscript message. Every C++ function member has a variable named. Every C++ function member has a variable named this. When that member’s message is sent to an object, the address of the receiving object is stored in. When that member’s message is sent to an object, the address of the receiving object is stored in this. mat1.Print(cin); Matrix::Print() this mat1

37 this (Ct’d) In a C++ function member, always contains the address of the object receiving the message. In a C++ function member, this always contains the address of the object receiving the message. Since it stores an address, can be thought of as pointing to the object receiving the message, and address-storing variables are commonly called pointers. Since it stores an address, this can be thought of as pointing to the object receiving the message, and address-storing variables are commonly called pointers. Since the value of is an address, we can’t use it as is to refer to the receiver of the message. Since the value of this is an address, we can’t use it as is to refer to the receiver of the message.

38 this (Ct’d) When applied to a pointer as a prefix operator, the asterisk () produces as its value the object pointed to. When applied to a pointer as a prefix operator, the asterisk ( * ) produces as its value the object pointed to. That is, if we use the notation: (*this) within a function member, the effect will be to access the receiver of the message (i.e., ourselves). To send ourselves the subscript message, we thus write: (*this)[r][c] which selects the whose index is within ourselves (and then sends that a second subscript message). which selects the Row whose index is r within ourselves (and then sends that Row a second subscript message).

39 Insertion cout << mat << endl; Overloading the insertion operator will let us display a Matrix in the “normal” manner: Since its left operand is an ostream, this function cannot be implemented as a function member. Specification: Receive: out, an ostream; mat, a Matrix. Output: the values in mat, via out. Passback: out, containing the Matrix. Return: out, for chaining.

40 Defining Insertion //... inline ostream & operator<<(ostream & out, const Matrix & mat) { mat.Print(out); // send mat the Print() msg return out; // allow chaining } Thanks to Print(), this is sufficiently simple to inline. We simply send our Matrix parameter the Print() message, and let it do the work...

41 Read() mat1.Read(cin); This member lets you read a matrix via an istream: Specification: Receive: in, an istream. Precondition: in contains the values of an m-by-n matrix, with each row on a separate line. Input: the matrix values, via in. Passback: in, the matrix values extracted from it. Postcondition: I contain the input values.

42 Defining Read() //... void Matrix::Read(istream & in) { double number; char separator; for (;;) // row-loop { Row aRow; // empty row for (;;) // column-loop { in >> number; // read number if (in.eof()) break; // quit if failed aRow.push_back(number); // append number in.get(separator); // read next char if (separator == ‘\n’) break;// quit if e-o-l } // end column-loop if (in.eof()) break; // quit if eof push_back(aRow); // append Row } // end row-loop } This is sufficiently complicated to define separately.

43 Extraction cin >> mat1; The extraction operator lets us read a Matrix from an istream, like any other object: Specification: Receive: in, an istream; mat, a Matrix. Precondition: myRows == m && myColumns == n && in contains the values of an m-by-n matrix, with one row/line. Input: the matrix, via in. Passback: in, the matrix read from it; mat, containing the extracted values. Return: in, for chaining.

44 Defining Extraction //... inline istream & operator>>(istream & in, Matrix & mat) { mat.Read(in); return in; } Thanks to Read(), this is simple enough to inline: We simply send our Matrix parameter the Read() message, and let it do the work...

45 Matrix Addition Matrix mat3 = mat1 + mat2; Defining the + operator will let us add matrices: Since its left operand is a Matrix, we can define operator+ as a Matrix function member, in which case such an expression will be treated as: Matrix mat3 = mat1.operator+(mat2);Specification: Receive: mat2, a Matrix. Precondition: mat2.Rows() == myRows && mat2.Columns() == myColumns. Return: mat3, containing the sum of myself and mat2.

46 Addition Operator //... Matrix Matrix::operator+(const Matrix & mat2) const { assert(mat2.Rows() == myRows && mat2.Columns() == myColumns); Matrix result(myRows, myColumns); for (int r = 0; r < myRows; r++) for (int c = 0; c < myColumns; c++) result[r][c] = (*this)[r][c] + mat2[r][c]; return result; } This is sufficiently complicated to define separately. Since the problem requires that we access all of the values in a 2-D structure, we use two nested for loops.

47 Our Program //... int main() { //... cin >> mat1; //... cin >> mat2; //... Matrix mat3 = mat1 + mat2; //... cout << mat3; //... } Our program will now work “as advertised”. All of our work is reuseable, and we can add more matrix-specific operations to class Matrix...

48 Summary If a new class is a special instance of an existing class, derivation can be used to define the new class. A derived class inherits all members (except constructors and destructors) of its parent class. A derived class constructor should use the parent class constructor to initialize inherited data members. In function members, is built-in variable containing the address of the receiver of the message. In function members, this is built-in variable containing the address of the receiver of the message. Within a function member, the expression refers to the object receiving the message. Within a function member, the expression (*this) refers to the object receiving the message.


Download ppt "Matrices Introducing Inheritance. Consider A matrix is a grid in which numbers can be stored. Algorithms for problems in scientific computing frequently."

Similar presentations


Ads by Google