Вы находитесь на странице: 1из 14

revised 2/26/03

The STL vector class.


The standard template library (STL) has a template class vector that includes most of the features we have already discussed for dynamic arrays. It also includes other features that we discuss below. A definition of this class sufficient for our purposes is shown in figure 1 where the identifier Vector is used in place of the actual STL class identifier vector. The data member vSize is the number of components of a Vector object. The data member vCapacity is the number of components allocated for the Vector object. Thus vCapacity is always greater than or equal to vSize. Whenever a Vector object is defined, vCapacity is put equal to vSize. For example, the definition
Vector<double> v(10, 1.0);

creates a vector v with double components, vSize = 10, vCapacity = 10 and each double component equal to 1.0. The constructor is labeled explicit to prevent any implicit conversion using the constructor. If the second parameter is missing in the above definition, the default constructor for the supplied type is used for the component values. If the supplied type is a predefined type, as it is in the example above, 0 is the default value. In a Vector class, the member functions front() and back() return references to, respectively, the components at position 0 and vSize - 1. There are two versions of each function, one read-or-write and the other read-only. All four functions could throw an exception when they are called with a Vector object whose size is zero. However, the STL vector class does not throw exceptions in this case and we have followed that design here. The member function reserve() can be used to increase the capacity of a vector object. When this is done, a new-delete sequence occurs with old values copied into the new array. Nothing happens if reserve() is called with an argument whose value is less than or equal to vCapacity. The function resize() first calls reserve() to insure vCapacity is large enough to handle the resize() parameter n. If n is less than the current vSize, vSize is changed to n without changing vCapacity. Components can be added and deleted easily from the back-end of a Vector object. Consequently, there are two special functions, push_back() and pop_back(), that do these respective tasks. If vSize < vCapacity, the function push_back() assigns v[vSize] to its argument and increments vSize by 1. Otherwise, the vector object first goes through a new-copy-delete sequence with vCapacity changed to 2 * vSize. The function pop_back() just decrements vSize by 1 unless it is already 0. In that case, pop_back() just The vector class. 1

returns without affecting any data members of the Vector object it is called with. The function operator=() can also change vCapacity. For example, if u and v are two Vector objects, and we write
u = v;

the components of v will be copied to the corresponding components of u if u.vCapacity > u.vSize. If the latter isn't true, u first goes through a new-copy-delete sequence with u.vCapacity equal to v.vSize. The only member function that can reduce a vector's capacity is swap(). Notice swap() is a member function of the Vector class. Even though the global swap() would accomplish the same thing as the member swap(), it is much slower because three Vector assignments take place. The relational operators are similar to those for the string class. In particular, the Boolean expression (u < v) is true if u is a prefix of v or (u[i] < v[i]) where i is the first component in which u and v differ. Iterators provide a way to sequentially go through the components of a data structure. The subscripts in the Vector class are a primary example of iterators. In fact, one might say that iterators of any class are an abstraction of the subscripts in the Vector class. To go through the components of a data structure, you need a beginning value for the iterator. This is supplied by the member function begin(). You also need a sentinel value to stop the sequential access. This is supplied by the member function end(). Here are two C++ segments that compare subscripts and iterators;
int n = 10; Vector<float> v(n); for (int i = 0; i != n; i++) v[i] = float(rand()) / RAND_MAX; int n = 10; Vector<float> v(n); for (Vector<float>::iterator ptr = v.begin(); ptr != v.end(); ptr++) *ptr = float(rand()) / RAND_MAX;

Subscript access is implemented in two ways in the Vector class. The usual one involving the subscript operator does not throw an exception if a subscript value is out of range. Instead, '[ ]' is overloaded for maximum efficiency by making it inline and not checking for exceptions. If the user wants range-checking to be in effect, the function at is provided. The complete header file for the Vector class is given in figure 1. A corresponding test file is shown in figure 2.

2 The vector class.

revised 2/26/03
#ifndef Vector_h #define Vector_h #include <limits> #include <exception> using namespace std; template <class T> class Vector { public: typedef unsigned int size_type; typedef T* iterator; typedef iterator const_iterator; explicit Vector (size_type n = 0, T u = T()); Vector (iterator start, iterator stop); Vector (const Vector& v); ~Vector(); Vector& operator= (const Vector& v); void assign(size_type n, const T& u); void assign(iterator start, iterator stop); // creates object with n components with values u; vCapacity = n // creates copy of Vector object's components from [start, stop) // copy constructor // destructor // assignment operator overload // gives implicit object size n with all components equal to u // gives implicit object components from Vector object's components [start, stop)

T& operator[] (size_type i) {return dataPtr[i];} // v[i] is the component labeled i; read and write version const T& operator[] (size_type i) const {return dataPtr[i];} // read-only version of v[i] T& at(size_type i); // equivalent to read/write version of [] only exceptions are thrown const T& at(size_type i) const; // equivalent to read-only version of [] only exceptions are thrown const T& back() const {return dataPtr[vSize -1];} const T& front() const {return dataPtr[0];} T& back() {return dataPtr[vSize -1];} T& front() {return dataPtr[0];} void pop_back() {vSize--;} void push_back (const T& t); // returns the back (last) component // returns the front (first) component // returns the last component; read and write version // returns the first component; read and write version // removes back (last) element // add 't' to back of Vector object

size_type size() const {return vSize;} size_type capacity() const {return vCapacity;} size_type max_size() const {return numeric_limits<unsigned int>::max()/sizeof(T);} bool empty() const {return !vSize;} void reserve(size_type n); void resize(size_type n, T u = T()); iterator begin() {return dataPtr;} iterator end() {return dataPtr + vSize;} const_iterator begin() const {return dataPtr;} const_iterator end() const {return dataPtr + vSize;} iterator erase(iterator ptr); iterator erase(iterator beginPtr, iterator endPtr); iterator insert(iterator ptr, const T& t); void insert(iterator ptr, size_type n, const T& t); void swap(Vector& v); void clear() {vSize = 0;} bool operator== (const Vector& y) const; bool operator!= (const Vector& y) const; bool operator< (const Vector& y) const; bool operator> (const Vector& y) const; bool operator<= (const Vector& y) const; bool operator>= (const Vector& y) const; private: size_type vSize; size_type vCapacity; T* dataPtr;}; // increases vCapacity to n; does nothing if current value is > n // changes vSize to n; new components, if present, have the value u // returns an iterator to the first (front) component // returns an iterator to one component 'beyond' the last (back) component // returns an iterator to the first (front) component // returns an iterator to one component beyond the last (back) component // deletes the component at ptr; back components move up // deletes components from beginPtr up to, but not including, endPtr // inserts the element t at ptr, moves back elements down // insert n t's beginning at ptr, moves back elements down // exchanges the implicit Vector object with v // vCapacity is unchanged // the equality operators are overloaded to get their 'string' equivalents // the relational operators are overloaded to get their 'string' equivalents

The vector class. 3

template <class T> Vector<T>::Vector(size_type n, T u) { int k = n; if (k < 0) throw length_error ("negative parameter in allocator call"); vCapacity = vSize = n; if (vCapacity) { dataPtr = new T[vCapacity]; // default constructor for T used here if (dataPtr == 0) throw bad_alloc(); } else dataPtr = 0; for (size_type i = 0; i < vSize; i++) dataPtr[i] = u; } // '=' for T used here template <class T> Vector<T>::Vector(iterator start, iterator stop) { int k = stop - start; if (k < 0) throw length_error ("improper iterator values"); vCapacity = vSize = k; if (vCapacity) { dataPtr = new T[vCapacity]; // default constructor for T used here if (dataPtr == 0) throw bad_alloc(); } else dataPtr = 0; for (size_type i = 0; i < vSize; i++) { dataPtr[i] = *start; // '=' for T used here start++; }} template <class T> Vector<T>::Vector(const Vector<T>& v) { vSize = v.vSize; vCapacity = v.vSize; if (vCapacity) { dataPtr = new T[vCapacity]; if (dataPtr == 0) throw bad_alloc(); } else dataPtr = 0; for (size_type i = 0; i < vSize; i++) dataPtr[i] = v.dataPtr[i]; } template <class T> Vector<T>::~Vector() { delete [] dataPtr; dataPtr = 0; }

// destructor for T used here

template <class T> Vector<T>& Vector<T>::operator= (const Vector<T>& v) { if (dataPtr != v.dataPtr) { if (vCapacity < v.vSize) { delete [] dataPtr; vCapacity = v.vSize; if (vCapacity) { dataPtr = new T[vCapacity]; if (dataPtr == 0) throw bad_alloc(); } else dataPtr = 0; } vSize = v.vSize; for (size_type i = 0; i < vSize; i++) dataPtr[i] = v.dataPtr[i]; } return *this; }

4 The vector class.

revised 2/26/03
template <class T> void Vector<T>::assign(size_type n, const T& u) { int k = n; if (k < 0) throw length_error ("nonpositive parameter in allocator call"); if (vCapacity < n) { delete [] dataPtr; vCapacity = n; dataPtr = new T[vCapacity]; } vSize = n; for (size_type i = 0; i < vSize; i++) dataPtr[i] = u; } template <class T> void Vector<T>::assign(iterator start, iterator stop) { vCapacity = stop - start; if (vCapacity < 0) throw length_error ("nonpositive parameter in allocator call"); vSize = stop - start; dataPtr = new T[vCapacity]; for (int i = 0; i < vSize; i++) { dataPtr[i] = *start; start++; }} template <class T> T& Vector<T>::at(size_type i) { if (0 > i or i >= vSize) throw out_of_range ("subscript out of range on write 'at'"); return dataPtr[i]; } template <class T> const T& Vector<T>::at(size_type i) const { if (0 > i or i >= vSize) throw out_of_range ("subscript out of range on read 'at'"); return dataPtr[i]; } template <class T> void Vector<T>::push_back (const T& t) { if (vSize == vCapacity) { vCapacity *= 2; if (vCapacity == 0) vCapacity = 1; T* ptr = new T[vCapacity]; if (ptr == 0) throw bad_alloc(); for (int i = 0; i < vSize; i++) ptr[i] = dataPtr[i]; delete [] dataPtr; dataPtr = ptr; } dataPtr[vSize++] = t; } template <class T> void Vector<T>::reserve(size_type n) { if (n == 0) return; if (vCapacity < n) { vCapacity = n; T* ptr = new T[vCapacity]; if (ptr == 0) throw bad_alloc(); for (int i = 0; i < vSize; i++) ptr[i] = dataPtr[i]; delete [] dataPtr; dataPtr = ptr; }} template <class T> void Vector<T>::resize(size_type n, T u) { int m = vCapacity; while (m < n) m *= 2; reserve(m); for (int i = vSize; i < n; i++) dataPtr[i] = u; vSize = n; }

The vector class. 5

template <class T> typename Vector<T>::iterator Vector<T>::erase(Vector<T>::iterator ptr) { // assumes begin() <= ptr < end() Vector<T>::iterator savePtr = ptr; if (vSize && ptr >= begin() && ptr < end()) { while (ptr + 1 != end()) { *ptr = *(ptr + 1); ptr++; } vSize--; } if (!vSize) dataPtr = 0; return savePtr; } template <class T> typename Vector<T>::iterator Vector<T>::erase(Vector<T>::iterator beginPtr, Vector<T>::iterator endPtr) { Vector<T>::iterator savePtr = beginPtr; if (vSize && beginPtr >= begin() && endPtr <= end() && beginPtr < endPtr) { Vector<T>::iterator ptr = end(); while (endPtr != ptr) { *beginPtr = *endPtr; beginPtr++; endPtr++; } vSize -= endPtr - beginPtr; if (!vSize) dataPtr = 0; } return savePtr; } template <class T> typename Vector<T>::iterator Vector<T>::insert(Vector<T>::iterator ptr, const T& t) { size_type offset = ptr - begin(); insert(ptr, 1, t); return begin() + offset; } template <class T> void Vector<T>::insert( Vector<T>::iterator ptr, size_type n, const T& t) { if (n == 0) return; size_type offset = ptr - begin(); reserve(vSize + n); for (Vector<T>::iterator i = end() - 1; i != begin() + offset -1 ; i--) *(i + n) = *i; for (Vector<T>::iterator i =begin() + offset; i != begin() + offset + n; i++) *i = t; vSize += n; } template <class T> void Vector<T>::swap(Vector<T>& v) { std::swap(dataPtr, v.dataPtr); std::swap(vSize, v.vSize); std::swap(vCapacity, v.vCapacity); } template <class T> bool Vector<T>::operator== (const Vector<T>& y) const { if (vSize != y.vSize) return false; for (Vector<T>::size_type i = 0; i < vSize; i++) if (dataPtr[i] != y.dataPtr[i]) return false; return true; } template <class T> bool Vector<T>::operator!= (const Vector<T>& y) const { if (vSize != y.vSize) return true; for (Vector<T>::size_type i = 0; i < vSize; i++) if (dataPtr[i] != y.dataPtr[i]) // '!=' used for T here return true; return false; }

6 The vector class.

revised 2/26/03
template <class T> bool Vector<T>::operator> (const Vector<T>& y) const { for (Vector<T>::size_type i = 0; (i != vSize) && (i != y.vSize); i++) { if (y.dataPtr[i] > dataPtr[i]) // '>' used for T here return false; if (dataPtr[i] > y.dataPtr[i]) return true; } if (y.vSize < vSize) return true; return false; } template <class T> bool Vector<T>::operator< (const Vector<T>& y) const { for (Vector<T>::size_type i = 0; (i != vSize) && (i != y.vSize); i++) { if (y.dataPtr[i] > dataPtr[i]) // '>' used for T here return true; if (dataPtr[i] > y.dataPtr[i]) return false; } if (vSize < y.vSize) return true; return false; } template <class T> bool Vector<T>::operator<= (const Vector<T>& y) const { for (Vector<T>::size_type i = 0; (i != vSize) && (i != y.vSize); i++) { if (y.dataPtr[i] > dataPtr[i]) // '>' used for T here return true; if (dataPtr[i] > y.dataPtr[i]) return false; } if (vSize <= y.vSize) return true; return false; } template <class T> bool Vector<T>::operator>= (const Vector<T>& y) const { for (Vector<T>::size_type i = 0; (i != vSize) && (i != y.vSize); i++) { if (y.dataPtr[i] > dataPtr[i]) // '>' used for T here return false; if (dataPtr[i] > y.dataPtr[i]) return true; } if (y.vSize <= vSize) return true; return false; } #endif

Figure 1. The header file Vector.h.


#include "Vector.h" #include <iostream> #include <cassert> #include <cstdlib> template <class T> ostream& operator<< (ostream& out, const Vector<T>& v); template <class T> ostream& operator<< (ostream& out, const Vector<T>& v) { for (Vector<T>::const_iterator i = v.begin(); i != v.end(); i++) out << *i << " "; return out; } template <class T> void TestConstFunctions(const Vector<T>& v); void VisualTests();

The vector class. 7

void AssignmentTests(); void SubscriptTests(); void TestClear_Swap(); void TestEndAccess(); void TestEraseFunctions(); void TestRelationalOperators(); void TestPush_Pop(); void TestReserveFunctions(); void TestExceptions(); int main() { VisualTests(); AssignmentTests(); SubscriptTests(); TestClear_Swap(); TestEndAccess(); TestEraseFunctions(); TestRelationalOperators(); TestPush_Pop(); TestReserveFunctions(); TestExceptions(); cout << "The tests are completed" << endl; } template <class T> void TestConstFunctions(const Vector<T>& v) { for (int i = 0; i < v.size(); i++) assert(v[i] == v.at(i)); if (v.size()) { assert(v[0] == v.front()); assert(v[v.size() - 1] == v.back()); }} void VisualTests() { // test constructors, '=', '==', '!=' Vector<float> v(5, 1); // check constructor and '<<' assert(v.size() == 5); assert(v.capacity() == 5); cout << "The output should be '1 1 1 1 1': " << v << endl; Vector<float> z(v.begin(), v.end());// check constructor assert(v == z); // check '==' assert(z.size() == 5); assert(z.capacity() == 5); cout << "The output should be '1 1 1 1 1': " << z << endl; Vector<float> u(10); // check constructor and '<<' assert(u.size() == 10); assert(u.capacity() == 10); cout << "The output should be '0 0 0 0 0 0 0 0 0 0': " << u << endl; assert(u != v); // check '!=' Vector<float> w(u); // check copy constructor and '<<' assert(w.size() == 10); assert(w.capacity() == 10); cout << "The output should be '0 0 0 0 0 0 0 0 0 0': " << w << endl; assert(w == u); // check '==' Vector<float> x; // check default constructor and '<<' assert(x.size() == 0); assert(x.capacity() == 0); cout << "The output should be blank: " << x << endl; Vector<float> t(x.begin(), x.end()); assert(t.size() == 0); assert(t.capacity() == 0); cout << "The output should be blank: " << t << endl; assert(t == x); x = w; // check '=' assert(x.size() == 10); assert(x.capacity() == 10); assert(x == w); cout << "The visual tests have been passed" << endl; }

8 The vector class.

revised 2/26/03

void AssignmentTests() { Vector<float> x, y, u(5), v(10, 2); x.assign(10, 2); assert(x == v); y.assign(5, 0); assert(y == u); y.assign(v.begin(), v.end()); assert(y == x); assert(y == v); u = y; assert(u == y); assert(u == x); assert(u == v); cout << "The assignment tests have been passed" << endl; } void SubscriptTests() { Vector<float> x(10, 1); Vector<float>::const_iterator ptr = x.begin(); for (int i = 0; i < x.size(); i++) { assert(x[i] == 1); assert(x.at(i) == 1); assert(*ptr == 1); ptr++;} TestConstFunctions(x); cout << "The subscript tests have been passed" << endl; } void TestClear_Swap() { Vector<float> u(10, 1), v(5), w; Vector<float> x(u), y(v); u.swap(v); assert(v.size() == 10); assert(v.capacity() == 10); assert(v == x); assert(u.size() == 5); assert(u.capacity() == 5); assert(u == y); w.swap(v); assert(v.empty()); assert(v.capacity() == 0); u.clear(); assert(u.size() == 0); assert(u.empty()); assert(u.capacity() == 5); cout << "The clear_swap tests have been passed" << endl; } void TestEndAccess() { Vector<float> v(10); for (int i = 0; i < 10; i++) v[i] = i; assert(v[0] == v.front()); assert(v[v.size() - 1] == v.back()); assert(v[0] == *v.begin()); Vector<float>::iterator ptr = v.end(); ptr--; assert(*ptr == v.back()); cout << "The end access tests have been passed" << endl; } void TestEraseFunctions() { Vector<float> v(5); for (int i = 0; i < v.size(); i++) v[i] = i; Vector<float>::iterator ptr = v.begin(); v.erase(ptr); cout << "The output should be '1 2 3 4': " << v << endl;

The vector class. 9

ptr = v.begin(); ptr++; v.erase(ptr); cout << "The output should be '1 3 4': " << v << endl; Vector<float> w(10); for (int i = 0; i < w.size(); i++) w[i] = i; Vector<float>::iterator beginPtr = w.begin() + 2; Vector<float>::iterator endPtr = w.end() - 1; w.erase(beginPtr, endPtr); cout << "The output should be '0 1 9': " << w << endl; w.resize(10); for (int i = 0; i < w.size(); i++) w[i] = i; w.insert(w.begin(), 4); cout << "The output should be '4 0 1 2 3 4 5 6 7 8 9': " << w << endl; w.insert(w.begin() + 2, 3, 7); cout << "The output should be '4 0 7 7 7 1 2 3 4 5 6 7 8 9': " << w << endl; cout << "The erase tests have been passed" << endl; } void TestRelationalOperators() { Vector<float> u, v(5), w(7), x; assert(u == x); assert(v > u); assert(w > u); assert(v >= u); assert(w >= u); assert(w > v); assert(v < w); assert(w >= v); assert(v <= w); assert(v != w); Vector<float> y(3, 1); x.assign(5, 0); assert(y > x); assert(y >= x); assert(x < y); assert(x <= y); assert(x != y); Vector<float> t(5); u.assign(5, 0); assert(t >= u); assert(u <= t); assert(t == u); cout << "The relational operator tests have been passed" << endl; } void TestPush_Pop() { Vector<float> w(10); // check pop_back() for (int i = 0; i < 10; i++) w.pop_back(); assert(w.empty()); // check empty() assert(w.capacity() == 10); // check capacity() for (int i = 0; i < 10; i++) // check push_back() and '[]' w.push_back(i); for (int i = 0; i < 10; i++) assert(w[i] == i); assert(w.size() == 10); // check size() for (int i = 0; i < 10; i++) w.push_back(i + 10); assert(w.capacity() == 20); assert(w.size() == 20); w.push_back(20); // check push_back() assert(w.capacity() == 40); assert(w.size() == 21); cout << "The push_pop tests have been passed" << endl; }

10 The vector class.

revised 2/26/03

void TestReserveFunctions() { Vector<float> w(40, 1); assert(w.capacity() == 40); assert(w.size() == 40); w.resize(10, 2); // check resize() and reserve() assert(w.capacity() == 40); assert(w.size() == 10); for (int i = 0; i < w.size(); i++) assert(w[i] == 1); w.resize(40, 2); for (int i = 0; i < 10; i++) assert(w[i] == 1); for (int i = 10; i < 40; i++) assert(w[i] == 2); w.resize(50, 2); assert(w.capacity() == 80); assert(w.size() == 50); for (int i = 0; i < 10; i++) assert(w[i] == 1); for (int i = 10; i < 50; i++) assert(w[i] == 2); cout << "The reserve tests have been passed" << endl; } void TestExceptions() { try { cout << "Check two-parameter constructor with negative size argument " << endl; Vector<float> v(-12, 1); } catch(length_error e) { cout << "throw caught: " << e.what() << endl; } try { cout << "Check two-parameter constructor with negative size argument " << endl; Vector<float> v(-12); } catch(length_error e) { cout << "throw caught: " << e.what() << endl; } try { cout << "Check one-parameter constructor with large size argument " << endl; Vector<float> v(2000000); } catch(bad_alloc e) { cout << "throw caught: " << e.what() << endl; } try { cout << "Check unlimited push_back() calls " << endl; Vector<float> v; while (true) v.push_back(1); } catch(bad_alloc e) { cout << "throw caught: " << e.what() << endl; } try { cout << "Check 'at' for an out of range call" << endl; Vector<float> v(10, 1); cout << v.at(10) << endl; } catch(out_of_range e) { cout << e.what() <<endl; } cout << "The exception tests are over" << endl; }

The vector class. 11

/* CodeWarrior Pro 8.3 output: The output should be '1 1 1 1 1': 1 1 1 1 1 The output should be '1 1 1 1 1': 1 1 1 1 1 The output should be '0 0 0 0 0 0 0 0 0 0': 0 0 0 0 0 0 0 0 The output should be '0 0 0 0 0 0 0 0 0 0': 0 0 0 0 0 0 0 0 The output should be blank: The output should be blank: The visual tests have been passed The assignment tests have been passed The subscript tests have been passed The clear_swap tests have been passed The end access tests have been passed The output should be '1 2 3 4': 1 2 3 4 The output should be '1 3 4': 1 3 4 The output should be '0 1 9': 0 1 9 The output should be '4 0 1 2 3 4 5 6 7 8 9': 4 0 1 2 3 4 5 The output should be '4 0 7 7 7 1 2 3 4 5 6 7 8 9': 4 0 7 7 The erase tests have been passed The relational operator tests have been passed The push_pop tests have been passed The reserve tests have been passed Check two-parameter constructor with negative size argument throw caught: negative parameter in allocator call Check two-parameter constructor with negative size argument throw caught: negative parameter in allocator call Check one-parameter constructor with large size argument Check unlimited push_back() calls throw caught: bad_alloc Check 'at' for an out of range call subscript out of range on write 'at' The exception tests are over The tests are completed */

0 0 0 0

6 7 8 9 7 1 2 3 4 5 6 7 8 9

Figure 2. The program VectorTest.cp. The execution times for member functions and other functions defined for the Vector class are easily expressed with the usual (,,)-notation. For example, suppose a Vector object has vSize equal to n. The time to erase a single component is a constant plus the time that it takes to move the components below it up one position. The number of components below the erased component varies between 0 and n - 1. We conclude the best-case time is (1) and the worst-case time is (n). If we pick a component 'at random' to erase, the average number of components moved is 1 (0 + 1+ L+ n 1) = (n 1) / 2 n We conclude that the average time to erase a single component is (n). We leave the analysis of the execution times of the remaining functions as an exercise. One of the best uses of the STL vector class is using it to implement a Matrix template class. A possible definition of the Matrix class is shown in figure 3 where, for simplicity, the type double is used for the components of the Matrix object and we've assumed the Matrix object has the same number of rows as it has columns. (It's not much 12 The vector class.

revised 2/26/03

harder to implement a Matrix template class for this case, but the class definition is easier to read with a predefined type.) We leave the definitions of the functions as an exercise.
class Matrix { friend bool operator== (const Matrix& A, const Matrix& B); friend bool operator!= (const Matrix& A, const Matrix& B); public: Matrix(unsigned int n = 1); // create an n by n matrix of all zeros Matrix(const Matrix& A); ~Matrix(); Matrix& operator= (const Matrix& A); // no overload is necessary if capacity is not a concern Matrix& operator+= (const Matrix& A); Matrix& operator-= (const Matrix& A); Matrix& operator*= (const Matrix& A); Matrix operator-() const; Matrix operator+() const; double& operator()(unsigned int i, unsigned int j); // access element (i, j) const T& operator()(unsigned int i, unsigned int j) const; const vector<T>& operator[] (unsigned int i) const; // usual access of element (i, j) vector<T>& operator[] (unsigned int i); unsigned int size() const; private: vector <vector <double> > v; }; ostream& operator<< (ostream& output, const Matrix& A); istream& operator>> (istream& input, Matrix& A); Matrix operator+ (const Matrix& A, const Matrix& B); // return A + B Matrix operator- (const Matrix& A, const Matrix& B); // return A - B Matrix operator* (const Matrix& A, const Matrix& B); // return A * B Matrix operator* (const T& a, const Matrix& B); // return a * B - scalar multiplication void IdentityMatrix (Matrix& A); // changes A to the identity matrix Matrix Power(const Matrix& A, unsigned int n); // return A * A * * A (n factors) Matrix Transpose(const Matrix& A); // returns the transpose of A

Figure 3. The definition of a square matrix class.

The vector class. 13

This should be enough arrays for anyone

14

Вам также может понравиться