From a6c375fc82bf487342f0327f4277b9f26153b27f Mon Sep 17 00:00:00 2001 From: James Yang Date: Wed, 13 Nov 2019 10:33:53 -0500 Subject: [PATCH 1/3] Add cpp_demo --- .gitignore | 3 +- M-cpp/cpp_demo/Makefile | 23 +++++++++ M-cpp/cpp_demo/README.md | 26 ++++++++++ M-cpp/cpp_demo/cpp_demo.cpp | 92 +++++++++++++++++++++++++++++++++ M-cpp/cpp_demo/matrix.cpp | 95 +++++++++++++++++++++++++++++++++++ M-cpp/cpp_demo/matrix.hpp | 34 +++++++++++++ M-cpp/cpp_demo/test_tools.hpp | 32 ++++++++++++ 7 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 M-cpp/cpp_demo/Makefile create mode 100644 M-cpp/cpp_demo/README.md create mode 100644 M-cpp/cpp_demo/cpp_demo.cpp create mode 100644 M-cpp/cpp_demo/matrix.cpp create mode 100644 M-cpp/cpp_demo/matrix.hpp create mode 100644 M-cpp/cpp_demo/test_tools.hpp diff --git a/.gitignore b/.gitignore index 3e13231..3ab2d1f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.o a.out .DS_Store - mylist.c +*cpp_demo +!cpp_demo/ diff --git a/M-cpp/cpp_demo/Makefile b/M-cpp/cpp_demo/Makefile new file mode 100644 index 0000000..660c5ed --- /dev/null +++ b/M-cpp/cpp_demo/Makefile @@ -0,0 +1,23 @@ +CC = g++ +CXX = g++ +CXXFLAGS = $(ADDED_CXXFLAGS) -std=c++11 -g -Wall $(INCLUDES) +LDFLAGS = -g + +.PHONY: default + +default: cpp_demo + +cpp_demo: matrix.o + +cpp_demo.o: matrix.hpp test_tools.hpp + +matrix.o: matrix.hpp + +.PHONY: clean + +clean: + rm -f cpp_demo *.o + +.PHONY: all + +all: clean default diff --git a/M-cpp/cpp_demo/README.md b/M-cpp/cpp_demo/README.md new file mode 100644 index 0000000..e59a8e8 --- /dev/null +++ b/M-cpp/cpp_demo/README.md @@ -0,0 +1,26 @@ +# Matrix Demo + +This demo shows how a user can define a matrix class in C++. +Similar to Jae's `MyString`, `Matrix` class defined in `matrix.hpp` encapsulates all the memory management as seen in the constructor and destructor. +We added a few more functionalities such as overloading `operator+` to show how we can also implement addition of two matrices. +Note that the user of this class `Matrix` does not need to know **how** memory management or addition are done, +but only that such functionalities exist. +This is one of the hallmarks of object-oriented programming (OOP). + +# Run +There are several tests defined in `cpp_demo.cpp` with the following names: +``` +CTOR: tests constructor and initialization of a Matrix object +CCTOR: tests copy constructor +CASS: tests copy assignment +ADD: tests addition of two matrices +``` + +Simply choose any subset of the tests and run the following command: +``` +make all ADDED_CXXFLAGS="-D -D ..." +``` +As an example, to run test `CTOR` and `ADD`, run: +``` +make all ADDED_CXXFLAGS="-DCTOR -DADD" +``` diff --git a/M-cpp/cpp_demo/cpp_demo.cpp b/M-cpp/cpp_demo/cpp_demo.cpp new file mode 100644 index 0000000..67edb0f --- /dev/null +++ b/M-cpp/cpp_demo/cpp_demo.cpp @@ -0,0 +1,92 @@ +#include +#include "matrix.hpp" +#include "test_tools.hpp" + +void test_ctor(const Matrix& M) +{ + utils::print_test("constructor"); + std::cout << "Printing M:" << std::endl; + // print contents + M.print(); +} + +void test_cctor(const Matrix& M) +{ + utils::print_test("copy constructor"); + // copy constructor + Matrix tmp = M; + std::cout << "Printing M:" << std::endl; + M.print(); + std::cout << "Printing copy constructed matrix:" << std::endl; + tmp.print(); +} + +void test_cass(const Matrix& M) +{ + utils::print_test("copy assignment"); + Matrix A(1, 2); + A(0,0) = 10; + A(0,1) = -3; + std::cout << "Printing A:" << std::endl; + A.print(); + std::cout << "Printing M:" << std::endl; + M.print(); + std::cout << "Printing A after copy assigned from M:" << std::endl; + // copy assignment + A = M; + A.print(); +} + +// Add M with a nontrivial matrix. +void test_add(const Matrix& M) +{ + utils::print_test("add"); + Matrix N(3, 2); + // initialization + for (int i = 0; i < N.nrows(); ++i) { + for (int j = 0; j < N.ncols(); ++j) { + N(i,j) = i + j; + } + } + Matrix res = M + N; + + std::cout << "Printing M:" << std::endl; + M.print(); + + std::cout << "Printing N:" << std::endl; + N.print(); + + std::cout << "Printing M + N:" << std::endl; + res.print(); +} + +int main() +{ + // constructor + Matrix M(3, 2); + // initialization + M(0,0) = 1; + M(0,1) = 3; + M(1,0) = 5; + M(1,1) = -2; + M(2,0) = 9; + M(2,1) = -5; + +#ifdef CTOR + test_ctor(M); +#endif + +#ifdef CCTOR + test_cctor(M); +#endif + +#ifdef CASS + test_cass(M); +#endif + +#ifdef ADD + test_add(M); +#endif + + return 0; +} diff --git a/M-cpp/cpp_demo/matrix.cpp b/M-cpp/cpp_demo/matrix.cpp new file mode 100644 index 0000000..3e32dd4 --- /dev/null +++ b/M-cpp/cpp_demo/matrix.cpp @@ -0,0 +1,95 @@ +#include "matrix.hpp" +#include +#include +#include + +// Constructor +Matrix::Matrix(int nrows, int ncols) + : nrows_(nrows), ncols_(ncols), data_(nullptr) +{ + data_ = new int[nrows_ * ncols_]; +} + +// Destructor +Matrix::~Matrix() +{ + delete[] data_; +} + +// Copy constructor +Matrix::Matrix(const Matrix& m) +{ + this->copy_helper(m); +} + +// Copy assignment +Matrix& Matrix::operator=(const Matrix& m) +{ + if (this == &m) { + return *this; + } + + delete[] this->data_; + this->copy_helper(m); + + return *this; +} + +// Helper function that deep copies another matrix into current matrix. +// This exact logic is used twice: copy constructor, copy assignment. +void Matrix::copy_helper(const Matrix& m) +{ + this->nrows_ = m.nrows_; + this->ncols_ = m.ncols_; + this->data_ = new int[this->nrows_ * this->ncols_]; + std::copy(m.data_, m.data_ + m.nrows_ * m.ncols_, this->data_); +} + +// operator() is overloaded to return the i,j element of the matrix +// If i >= number of rows or j >= number of columns, program crashes (in practice we want to raise error). +int& Matrix::operator()(int i, int j) +{ + assert(i < this->nrows_ && j < this->ncols_); + return data_[i * ncols_ + j]; +} + +const int& Matrix::operator()(int i, int j) const +{ + return const_cast(*this)(i, j); +} + +// operator+ is overloaded to return a new matrix that is the mathematical +// addition of current matrix and another matrix. +// If the sizes do not match, program crashes (in practice we want to raise error). +Matrix Matrix::operator+(const Matrix& m) const +{ + assert(this->nrows_ == m.nrows_); + assert(this->ncols_ == m.ncols_); + Matrix res(this->nrows_, this->ncols_); + for (int i = 0; i < this->nrows_; ++i) { + for (int j = 0; j < this->ncols_; ++j) { + res(i,j) = (*this)(i,j) + m(i,j); + } + } + return res; +} + +//////////////////////////////////////////////// +// DO NOT WORRY ABOUT CODE FROM HERE +//////////////////////////////////////////////// + +// Prints the contents of the matrix +void Matrix::print() const +{ + std::cout << nrows_ << " x " << ncols_ << std::endl; + std::cout << "[" << std::endl; + for (int i = 0; i < nrows_; ++i) { + for (int j = 0; j < ncols_; ++j) { + std::cout << std::setfill(' ') + << std::setw(5) + << (*this)(i,j); + } + std::cout << std::endl; + } + std::cout << "]" << std::endl; +} diff --git a/M-cpp/cpp_demo/matrix.hpp b/M-cpp/cpp_demo/matrix.hpp new file mode 100644 index 0000000..e3729e1 --- /dev/null +++ b/M-cpp/cpp_demo/matrix.hpp @@ -0,0 +1,34 @@ +#pragma once + +// Matrix is a class that represents a matrix of integers. +class Matrix +{ +public: + Matrix() =default; + Matrix(int nrows, int ncols); + Matrix(const Matrix&); + ~Matrix(); + Matrix& operator=(const Matrix&); + + int& operator()(int i, int j); + const int& operator()(int i, int j) const; + + Matrix operator+(const Matrix&) const; + + void print() const; + int nrows() const + { + return nrows_; + } + int ncols() const + { + return ncols_; + } + +private: + void copy_helper(const Matrix&); + + int nrows_; + int ncols_; + int* data_; +}; diff --git a/M-cpp/cpp_demo/test_tools.hpp b/M-cpp/cpp_demo/test_tools.hpp new file mode 100644 index 0000000..a56fde4 --- /dev/null +++ b/M-cpp/cpp_demo/test_tools.hpp @@ -0,0 +1,32 @@ +#pragma once +//////////////////////////////////////////////// +// DO NOT WORRY ABOUT CODE FROM HERE +//////////////////////////////////////////////// + +#include +#include + +namespace utils { + +// Length of the test name message +const size_t TESTLENGTH = 100; +const std::string EXTRAMSG= " test"; +const char FILLCHAR = '='; + +// Beautifully prints test name +// Prints 100 characters centered with test name and the rest filled with '*' +inline void print_test(const std::string& testname) +{ + size_t fillsize = (TESTLENGTH - testname.size() - EXTRAMSG.size()) / 2; + std::cout << std::setfill(FILLCHAR) + << std::setw(fillsize) + << "" + << std::left + << std::setfill(FILLCHAR) + << std::setw(TESTLENGTH - fillsize) + << testname + EXTRAMSG + << std::endl + << std::right; +} + +} // namespace utils From f8c825b155d5269ca8eba9640e5d09099d5d3a7c Mon Sep 17 00:00:00 2001 From: James Yang Date: Wed, 13 Nov 2019 18:58:31 -0500 Subject: [PATCH 2/3] Apply John's comments --- M-cpp/cpp_demo/Makefile | 4 +- M-cpp/cpp_demo/README.md | 20 ++---- M-cpp/cpp_demo/cpp_demo.cpp | 113 ++++++++++++++++++---------------- M-cpp/cpp_demo/matrix.cpp | 46 ++++++-------- M-cpp/cpp_demo/matrix.hpp | 54 ++++++++++++---- M-cpp/cpp_demo/test_tools.hpp | 32 ---------- 6 files changed, 128 insertions(+), 141 deletions(-) delete mode 100644 M-cpp/cpp_demo/test_tools.hpp diff --git a/M-cpp/cpp_demo/Makefile b/M-cpp/cpp_demo/Makefile index 660c5ed..8daaf45 100644 --- a/M-cpp/cpp_demo/Makefile +++ b/M-cpp/cpp_demo/Makefile @@ -1,6 +1,6 @@ CC = g++ CXX = g++ -CXXFLAGS = $(ADDED_CXXFLAGS) -std=c++11 -g -Wall $(INCLUDES) +CXXFLAGS = -std=c++11 -g -Wall $(INCLUDES) LDFLAGS = -g .PHONY: default @@ -9,7 +9,7 @@ default: cpp_demo cpp_demo: matrix.o -cpp_demo.o: matrix.hpp test_tools.hpp +cpp_demo.o: matrix.hpp matrix.o: matrix.hpp diff --git a/M-cpp/cpp_demo/README.md b/M-cpp/cpp_demo/README.md index e59a8e8..9884b78 100644 --- a/M-cpp/cpp_demo/README.md +++ b/M-cpp/cpp_demo/README.md @@ -7,20 +7,8 @@ Note that the user of this class `Matrix` does not need to know **how** memory m but only that such functionalities exist. This is one of the hallmarks of object-oriented programming (OOP). -# Run -There are several tests defined in `cpp_demo.cpp` with the following names: -``` -CTOR: tests constructor and initialization of a Matrix object -CCTOR: tests copy constructor -CASS: tests copy assignment -ADD: tests addition of two matrices -``` +### Matrix Addition +See the following [link](https://en.wikipedia.org/wiki/Matrix_(mathematics)#Addition,_scalar_multiplication,_and_transposition) for an explanation and an example of how to add two matrices. -Simply choose any subset of the tests and run the following command: -``` -make all ADDED_CXXFLAGS="-D -D ..." -``` -As an example, to run test `CTOR` and `ADD`, run: -``` -make all ADDED_CXXFLAGS="-DCTOR -DADD" -``` +## Run +Type `make` to build the executable and run `./cpp_demo`. diff --git a/M-cpp/cpp_demo/cpp_demo.cpp b/M-cpp/cpp_demo/cpp_demo.cpp index 67edb0f..01b66f0 100644 --- a/M-cpp/cpp_demo/cpp_demo.cpp +++ b/M-cpp/cpp_demo/cpp_demo.cpp @@ -1,53 +1,82 @@ #include #include "matrix.hpp" -#include "test_tools.hpp" -void test_ctor(const Matrix& M) +static const std::string BARS = "==================="; + +// Prints testname formatted as such: +// ===================testname test=================== +void print_test(const std::string& testname) { - utils::print_test("constructor"); - std::cout << "Printing M:" << std::endl; - // print contents - M.print(); + std::cout << BARS << testname << " test" << BARS << std::endl; } -void test_cctor(const Matrix& M) +// Creates a matrix of size 3 x 2 and initializes entries. +// Returns the created matrix. +Matrix make_matrix() { - utils::print_test("copy constructor"); - // copy constructor - Matrix tmp = M; + Matrix M(3, 2); + M(0,0) = 1; M(0,1) = 3; + M(1,0) = 5; M(1,1) = -2; + M(2,0) = 9; M(2,1) = -5; + return M; +} + +// Tests constructor. +// Prints the contents of a constructed and initialized matrix. +void test_ctor() +{ + print_test("constructor"); + Matrix M(1, 2); + M(0,0) = 6; M(0,1) = 9; std::cout << "Printing M:" << std::endl; - M.print(); + M.print(); // prints the contents of M +} + +// Tests copy constructor. +// Creates a copy constructed Matrix object from return value of make_matrix(). +// Prints the contents of copy constructed object. +void test_cctor() +{ + print_test("copy constructor"); + Matrix M = make_matrix(); // copy constructed std::cout << "Printing copy constructed matrix:" << std::endl; - tmp.print(); + M.print(); } -void test_cass(const Matrix& M) +// Tests copy assignment. +// Creates Matrix M from return value of make_matrix(), Matrix A, and copy assigns M to A. +// Prints the contents of M and the contents of A before and after copy assignment. +void test_cass() { - utils::print_test("copy assignment"); + print_test("copy assignment"); + Matrix M = make_matrix(); Matrix A(1, 2); - A(0,0) = 10; - A(0,1) = -3; + A(0,0) = 10; A(0,1) = -3; std::cout << "Printing A:" << std::endl; A.print(); std::cout << "Printing M:" << std::endl; M.print(); std::cout << "Printing A after copy assigned from M:" << std::endl; - // copy assignment - A = M; + A = M; // copy assignment A.print(); } -// Add M with a nontrivial matrix. -void test_add(const Matrix& M) +// Tests adding Matrix M with a nontrivial matrix. +// We chose the nontrivial matrix to be +// [ +// 0, 1 +// 1, 2 +// 2, 3 +// ] +void test_add() { - utils::print_test("add"); + print_test("add"); + Matrix M = make_matrix(); Matrix N(3, 2); - // initialization - for (int i = 0; i < N.nrows(); ++i) { - for (int j = 0; j < N.ncols(); ++j) { - N(i,j) = i + j; - } - } + N(0,0) = 0; N(0,1) = 1; + N(1,0) = 1, N(1,1) = 2; + N(2,0) = 2; N(2,1) = 3; + Matrix res = M + N; std::cout << "Printing M:" << std::endl; @@ -62,31 +91,11 @@ void test_add(const Matrix& M) int main() { - // constructor - Matrix M(3, 2); - // initialization - M(0,0) = 1; - M(0,1) = 3; - M(1,0) = 5; - M(1,1) = -2; - M(2,0) = 9; - M(2,1) = -5; - -#ifdef CTOR - test_ctor(M); -#endif - -#ifdef CCTOR - test_cctor(M); -#endif - -#ifdef CASS - test_cass(M); -#endif - -#ifdef ADD - test_add(M); -#endif + // run tests + test_ctor(); + test_cctor(); + test_cass(); + test_add(); return 0; } diff --git a/M-cpp/cpp_demo/matrix.cpp b/M-cpp/cpp_demo/matrix.cpp index 3e32dd4..3020c96 100644 --- a/M-cpp/cpp_demo/matrix.cpp +++ b/M-cpp/cpp_demo/matrix.cpp @@ -1,28 +1,26 @@ -#include "matrix.hpp" #include -#include #include +#include "matrix.hpp" -// Constructor Matrix::Matrix(int nrows, int ncols) - : nrows_(nrows), ncols_(ncols), data_(nullptr) + // good practice to initialize members regardless of constructor body + : nrows_(nrows), ncols_(ncols), data_(nullptr) { data_ = new int[nrows_ * ncols_]; } -// Destructor Matrix::~Matrix() { delete[] data_; } -// Copy constructor Matrix::Matrix(const Matrix& m) { - this->copy_helper(m); + // good practice to use this keyword to make it clear + // what is associated with current object. + this->copy_from(m); } -// Copy assignment Matrix& Matrix::operator=(const Matrix& m) { if (this == &m) { @@ -30,14 +28,12 @@ Matrix& Matrix::operator=(const Matrix& m) } delete[] this->data_; - this->copy_helper(m); + this->copy_from(m); return *this; } -// Helper function that deep copies another matrix into current matrix. -// This exact logic is used twice: copy constructor, copy assignment. -void Matrix::copy_helper(const Matrix& m) +void Matrix::copy_from(const Matrix& m) { this->nrows_ = m.nrows_; this->ncols_ = m.ncols_; @@ -45,49 +41,47 @@ void Matrix::copy_helper(const Matrix& m) std::copy(m.data_, m.data_ + m.nrows_ * m.ncols_, this->data_); } -// operator() is overloaded to return the i,j element of the matrix -// If i >= number of rows or j >= number of columns, program crashes (in practice we want to raise error). int& Matrix::operator()(int i, int j) { - assert(i < this->nrows_ && j < this->ncols_); + assert(i < nrows_ && j < ncols_); return data_[i * ncols_ + j]; } const int& Matrix::operator()(int i, int j) const { + // Both idiomatic and safer to use const_cast to cast away constness + // than performing C-style casting like ((Matrix&) *this). + // Be very careful with const_cast; this is one of the few times that its usage is considered acceptable. return const_cast(*this)(i, j); } -// operator+ is overloaded to return a new matrix that is the mathematical -// addition of current matrix and another matrix. -// If the sizes do not match, program crashes (in practice we want to raise error). Matrix Matrix::operator+(const Matrix& m) const { + // assert same sizes assert(this->nrows_ == m.nrows_); assert(this->ncols_ == m.ncols_); + Matrix res(this->nrows_, this->ncols_); + for (int i = 0; i < this->nrows_; ++i) { for (int j = 0; j < this->ncols_; ++j) { res(i,j) = (*this)(i,j) + m(i,j); } } + return res; } -//////////////////////////////////////////////// -// DO NOT WORRY ABOUT CODE FROM HERE -//////////////////////////////////////////////// - -// Prints the contents of the matrix void Matrix::print() const { + // Print dimensions of matrix std::cout << nrows_ << " x " << ncols_ << std::endl; + + // Print contents of matrix std::cout << "[" << std::endl; for (int i = 0; i < nrows_; ++i) { for (int j = 0; j < ncols_; ++j) { - std::cout << std::setfill(' ') - << std::setw(5) - << (*this)(i,j); + std::cout << (*this)(i,j) << ' '; } std::cout << std::endl; } diff --git a/M-cpp/cpp_demo/matrix.hpp b/M-cpp/cpp_demo/matrix.hpp index e3729e1..6f56c48 100644 --- a/M-cpp/cpp_demo/matrix.hpp +++ b/M-cpp/cpp_demo/matrix.hpp @@ -1,34 +1,62 @@ +// pragma once is the C++ way of include guards. +// This guarantees that header files will be included at most once. #pragma once // Matrix is a class that represents a matrix of integers. class Matrix { public: - Matrix() =default; - Matrix(int nrows, int ncols); - Matrix(const Matrix&); - ~Matrix(); - Matrix& operator=(const Matrix&); + Matrix() =default; // tells compiler to generate default constructor. + // Note that if user provides a constructor (as shown in the next line of code), + // compiler will not generate a default constructor! + // Any declarations such as: + // Matrix M; + // will raise a compiler error since there is no default constructor. + // Sometimes you may not want your Matrix to have this default constructor. + // In this case, you may omit this line, or + // as good practice, replace =default with =delete to be more explicit. + Matrix(int nrows, int ncols); // constructor + Matrix(const Matrix&); // copy constructor + ~Matrix(); // destructor + Matrix& operator=(const Matrix&); // copy assignment operator - int& operator()(int i, int j); - const int& operator()(int i, int j) const; + // operator() returns the row i, column j element of the matrix. + // For simplicity, it is defined such that if i >= number of rows or j >= number of columns, + // program crashes; in practice, it is best to raise an error. + // We return a reference to an internal data (int) so that we may write code as such: + // + // M(1, 2) = 69; + // + // which assigns 69 to row 2, column 3 entry of matrix M. + // If operator() returns an int (no reference) the above code does not even compile. + int& operator()(int i, int j); + const int& operator()(int i, int j) const; // const version - Matrix operator+(const Matrix&) const; + // operator+ returns a new matrix that is the matrix addition of current matrix and matrix M. + // If the sizes do not match, program crashes; in practice, it is best to raise an error. + Matrix operator+(const Matrix& M) const; - void print() const; + // Prints contents of current matrix. + void print() const; + + // Returns the number of rows. int nrows() const { return nrows_; } + + // Returns the number of columns. int ncols() const { return ncols_; } private: - void copy_helper(const Matrix&); + // Helper function that deep copies another matrix into current matrix. + // This exact logic is used in copy constructor and copy assignment. + void copy_from(const Matrix&); - int nrows_; - int ncols_; - int* data_; + int nrows_; // number of rows + int ncols_; // number of columns + int* data_; // pointer to array containing data }; diff --git a/M-cpp/cpp_demo/test_tools.hpp b/M-cpp/cpp_demo/test_tools.hpp deleted file mode 100644 index a56fde4..0000000 --- a/M-cpp/cpp_demo/test_tools.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -//////////////////////////////////////////////// -// DO NOT WORRY ABOUT CODE FROM HERE -//////////////////////////////////////////////// - -#include -#include - -namespace utils { - -// Length of the test name message -const size_t TESTLENGTH = 100; -const std::string EXTRAMSG= " test"; -const char FILLCHAR = '='; - -// Beautifully prints test name -// Prints 100 characters centered with test name and the rest filled with '*' -inline void print_test(const std::string& testname) -{ - size_t fillsize = (TESTLENGTH - testname.size() - EXTRAMSG.size()) / 2; - std::cout << std::setfill(FILLCHAR) - << std::setw(fillsize) - << "" - << std::left - << std::setfill(FILLCHAR) - << std::setw(TESTLENGTH - fillsize) - << testname + EXTRAMSG - << std::endl - << std::right; -} - -} // namespace utils From cb930991f03c1a188f90672d8b5f8911bbbb2043 Mon Sep 17 00:00:00 2001 From: James Yang Date: Fri, 15 Nov 2019 11:14:30 -0500 Subject: [PATCH 3/3] Apply John's second round of comments --- M-cpp/cpp_demo/matrix.cpp | 9 +++++++-- M-cpp/cpp_demo/matrix.hpp | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/M-cpp/cpp_demo/matrix.cpp b/M-cpp/cpp_demo/matrix.cpp index 3020c96..2131dfb 100644 --- a/M-cpp/cpp_demo/matrix.cpp +++ b/M-cpp/cpp_demo/matrix.cpp @@ -16,17 +16,22 @@ Matrix::~Matrix() Matrix::Matrix(const Matrix& m) { - // good practice to use this keyword to make it clear - // what is associated with current object. this->copy_from(m); } Matrix& Matrix::operator=(const Matrix& m) { + // If m is the same object as current object, + // there is nothing to do but return reference to current object. + // If this check were not done and continued with code, + // we would free this->data_ and copy contents of m.data_, + // but m.data_ points to freed memory since m and *this are the same objects! if (this == &m) { return *this; } + // MUST free existing data before reassigning + // Otherwise, memory leak! delete[] this->data_; this->copy_from(m); diff --git a/M-cpp/cpp_demo/matrix.hpp b/M-cpp/cpp_demo/matrix.hpp index 6f56c48..916c3a6 100644 --- a/M-cpp/cpp_demo/matrix.hpp +++ b/M-cpp/cpp_demo/matrix.hpp @@ -27,7 +27,7 @@ class Matrix // // M(1, 2) = 69; // - // which assigns 69 to row 2, column 3 entry of matrix M. + // which assigns 69 to row 1, column 2 entry of matrix M. // If operator() returns an int (no reference) the above code does not even compile. int& operator()(int i, int j); const int& operator()(int i, int j) const; // const version