Polynomial
A.First Edition
This is my first edition of a simple assignment of C++. And I hesitate for some time to decide whether to add it.
1. I defined a series of enum type ErrorType to enable error handling within one function. As I observe that different constructor and other member functions repeatedly require these error handlers. 2. I defined a series of enum type ArithType to enable operator overloading of +,-*,= to be implemented through one same functions as they are almost same operations. 3. During assignment operation, I think old data should be preserved in case assignment failed, and old data should be restored. 4. The term of order is stored ascendly. 5. Many utility methods are defined to maximumly reduce repeating codes. 6. Since 0 term should not be displayed, for an object created by default constructor will be initialized to all 0's. So, friend function cout<<P cannot display anything.
1. bool Polynomial::initialize(int Order)
This method takes care of two kinds of error:
a) Input parameter Order is negative;
b) Dynamic allocating of memory failed.
2. const Polynomial& Polynomial::operator=(const Polynomial& P)
This method I insist to store the old pointer and old order in case assignment fails, the old data will not be
destroyed!
3. ostream& operator<<(ostream& in, const Polynomial& P)
This friend function has an assumption that the highest order term must be NON-Zero term! Then all non-zero
consecutive term will be correctly expressed.
4. const Polynomial& Polynomial::operator=(const Polynomial& P)
a) First of all self-assignment not allowed.
b) Dynamic allocation only needed when order are not same.
c) You only need to delete old pointer after you succeed in copying data and of course you allocate new memory.
Please note the case that the order of two objects are same, don't delete!
d) In all cases of failure, original data should remain unchanged!
E.Further improvement
1. Are you kidding? For such a trivial program?
polynomial.h
#ifndef POLYNOMIAL_H #define POLYNOMIAL_H #include <iostream> using std::ostream ; //this makes error handling easier enum ErrorType {UnableAllocateMemory, OrderNonPositive, InvalidArray, SelfAssignmentNotAllowed, SubscriptExceedOrder}; enum ArithType {Addition, Subtraction, Multiply, Assignment}; class Polynomial { // STREAM EXTRACTION OPERATOR friend ostream& operator<<( ostream&, const Polynomial& ) ; public: // CONSTRUCTOR - Creates Polynomial of order O Polynomial( int Order = 0 ) ; // CONSTRUCTOR - Creates Polynomial of order O Initializing the // coefficients to the coef array Polynomial( float coef[], int Order = 0 ) ; // COPY CONSTRUCTOR - Copies the polynomial into "this" Polynomial( const Polynomial& P ) ; // DESTRUCTOR - Clean up dynamically created data ~Polynomial() ; const Polynomial& operator=( const Polynomial& ) ; Polynomial operator+( const Polynomial& ) const ; Polynomial operator-( const Polynomial& ) const ; Polynomial operator*( const Polynomial& ) const ; // You're lucky I didn't make you do a DIVIDE function... // ... Maybe I'll save it for the FINAL :) // SUBSCRIPT OVERLOAD - Use this operator to change any of the // coefficients after the Polynomial has been created. float& operator[]( unsigned int sub ) ; int getOrder(){return order;} private: void showMessage(ErrorType errorType); void errorHandler(ErrorType errorType); bool initialize(int Order); void uninitialize(); bool arithOperation(const float* ptr, const int size, ArithType arithType=Assignment); Polynomial doArithOp(const Polynomial& P, ArithType) const; int order ; // Polynomial Order // polyPtr will point to an array of polynomial coefficients // Don't forget that a polynomial of Order N has N+1 terms // Polynomial is created "dynamically" (i.e. use "new") float* polyPtr ; } ; #endif
กก
กก
polynomial.cpp
/***************************************************** Course: COEN244 Assignment No.: 3 Program: Polynomial Name: Qingzhe Huang ID: 5037735 Major features: 1. I defined a series of enum type ErrorType to enable error handling within one function. As I observe that different constructor and other member functions repeatedly require these error handlers. 2. I defined a series of enum type ArithType to enable operator overloading of +,-*,= to be implemented through one same functions as they are almost same operations. 3. During assignment operation, I use a kind of SAFE ASSIGNMENT. I think old data should be preserved in case assignment failed, and old data should be restored. 4. The term of order is stored ascendingly. 5. Many utility methods are defined to maximumly reduce repeating codes. 6. Since 0 term should not be displayed, for an object created by default constructor will be initialized to all 0's. So, friend function cout<<P can only display one 0. *******************************************************/ #include "Polynomial.h" using namespace std; //I don't know why compiler complains when I place this in .h file!!! char* errorMessage[5] = {"Unable to allocate memory!", "Order cannot be negative number!", "Invalid array input", "Self-assignment not allowed", "Subscipt exceeds order limit!"}; //My idea is to display the first term and all non-zero term will be displayed with //"+" sign at beginning. This assumes the highest term is always non-zero, but //in case of subtraction, it may be incorrect!!!! ostream& operator<<(ostream& in, const Polynomial& P) { bool first=true; for (int i=P.order; i>=0; i--) { if (P.polyPtr[i]!=0)//only show non-zero term { //only print the first non-zero term if (first)//the first non-zero term is print without "+" sign { in<<P.polyPtr[i]<<"x^"<<i; first = false; } else { if (i>1) { //all consecutive term has + at beginning in<<" + "<<P.polyPtr[i]<<"x^"<<i; } else { if (i==1) { in<<" + "<<P.polyPtr[i]<<"x"; } else { in<<" + "<<P.polyPtr[i]; //the last term is constant term } } } } else { if (first && i==0)//if all term are 0, at least print one 0 { in<<0; } } } cout<<endl; return in; } Polynomial::Polynomial(int Order) { initialize(Order); } //in ascending order does it store Polynomial::Polynomial(const Polynomial& P) { if (initialize(P.order))//test if allocate memory is ok { if (!arithOperation(P.polyPtr, P.order+1))//default is assginment { uninitialize(); } } } Polynomial::Polynomial(float coef[], int Order) { //only do the assignment after initializing succeedingly if (initialize(Order)) { if (!arithOperation(coef, order+1)) { uninitialize();//in case assignment failed, ptr should be cleared to NULL } } } Polynomial::~Polynomial() { uninitialize(); } //this utility method can be used for different constructors bool Polynomial::initialize(int Order) { if (Order>=0) { order = Order; polyPtr = new float[Order+1]; if (polyPtr==NULL) { errorHandler(UnableAllocateMemory);//we must make sure user won't use this array //by mistake! //casue maybe some method is dependend on checking the order to see //if array is usable. So in this case I make order =-1; } else { //do we need to initialize all elements of array? I guess so. for (int i=0; i<=order; i++) { polyPtr[i] = 0; } return true; } } else { errorHandler(OrderNonPositive); } return false; } float& Polynomial::operator [](unsigned int sub) { if (sub>order+1) { errorHandler(SubscriptExceedOrder); return polyPtr[0]; //I don't know if we can have better choice } else { return polyPtr[sub]; } } Polynomial Polynomial::operator *(const Polynomial& P) const { return doArithOp(P, Multiply); } Polynomial Polynomial::operator+(const Polynomial& P) const { return doArithOp(P, Addition); } Polynomial Polynomial::operator -(const Polynomial& P) const { return doArithOp(P, Subtraction); } //this utility method make sure to initialize result to correct order for arithmatic op //as return value must be a const for the return value of operator +,-,*, I have to make //it a const function, and this is very ackward!!! Polynomial Polynomial::doArithOp(const Polynomial& P, ArithType arithType) const { int newOrder = order>P.order?order:P.order; Polynomial Result; if (Result.initialize(newOrder)) { if (Result.arithOperation(this->polyPtr, this->order+1))//default is assignment { if (Result.arithOperation(P.polyPtr, P.order+1, arithType)) { return Result; } } } return Result; //always return an object, this is different with my original design //return NULL; } //I am using a kind of SAFE assignment. If there is any exception during dynamic allocation //or copying data, the original data will not be destroyed, on the contrary, it will be safely //restored. So, even there is a failure in assignment, the object will remain the original //state, not with a lost-memory pointer. const Polynomial& Polynomial::operator=(const Polynomial& P) { if (this!=&P)//see if assign to itself { float* oldPtr = polyPtr;//should restore ptr in case copy fails int oldOrder = order; //store old order to restore if assign fails if (order!=P.order)//only do resize if size is different { if (initialize(P.order))//see if any exception during mem allocation { if (arithOperation(P.polyPtr, P.order+1)) { delete[] oldPtr;//succeed then destroy old ptr return *this; } } } else { if (arithOperation(P.polyPtr, P.order+1))//default is copy or assignment { return *this; } } //in all other cases, old data need be restored polyPtr = oldPtr; order = oldOrder; } else { errorHandler(SelfAssignmentNotAllowed); } return *this; } void Polynomial::uninitialize() { delete [] polyPtr; polyPtr = NULL; order =-1; //as even order is 0, it does not mean 0 element in polyPtr, //order =0 means there is one element and polyPtr is not NULL } //I put all error handle method together to make it easy to maintain void Polynomial::errorHandler(ErrorType errorType) { //in case there is error, I will restore all intialized status //make pointer NULL, counter 0 switch (errorType) { case UnableAllocateMemory: showMessage(UnableAllocateMemory); order =-1; break; case OrderNonPositive: showMessage(OrderNonPositive); polyPtr =NULL; order = -1; break; case InvalidArray: showMessage(InvalidArray); break; case SelfAssignmentNotAllowed: //this is a minor error, only message showed showMessage(SelfAssignmentNotAllowed); break; case SubscriptExceedOrder: showMessage(SubscriptExceedOrder);//showmessage is enough for this minor error. break; } } //this utility method saves the repeating code void Polynomial::showMessage(ErrorType errorType) { cout<<errorMessage[errorType]<<endl; } //the caller of arithOperation will make sure size no exceed limit //default arithType is assignment bool Polynomial::arithOperation(const float* ptr, const int size, ArithType arithType) { if (ptr==NULL) { errorHandler(InvalidArray); return false; } else { for (int i=0; i<size; i++) { //in ascending order switch (arithType) { case Addition: polyPtr[i] +=ptr[i]; break; case Subtraction: polyPtr[i] -=ptr[i]; break; case Multiply: polyPtr[i] *=ptr[i]; break; case Assignment: polyPtr[i] =ptr[i]; break; } } return true; } }
driver.cpp
กก
#include <iostream> #include "Polynomial.h" using namespace std; int main() { float f1[] = {1.3,3.2, 4.3, 0, 6.5,7.6}; float f2[] = {2.3, 4.5, 6.7, 5}; //test default constructor Polynomial P; cout<<"test default constructor, " <<"and P should be order of 0 with one term initialized to 0:\n"; cout<<"order of P:"<<P.getOrder()<<endl; cout<<"all element of P is:\n"<<P; //test constructor with float array as input cout<<"test constructor with float array as input, now Q should be:\n"; Polynomial Q(f1, 5); cout<<Q; cout<<"\nTest friend function and term of 0 will not\n" <<"be showed--term of order of 3 is 0 and will not be showed:\n"<<Q<<endl; //test copy constructor cout<<"now test copy constructor, R will be initialized same as Q\n"; Polynomial R(Q); cout<<"\nnow Q is\n"<<Q; cout<<"\nnow R should be same as Q\n"<<R; //test conversion constructor cout<<"\nnow test conversion constructor, R should be initialized to order of 4\n" <<"by R = 4, and all term is initialized to 0 and R is:\n"<<(R = 4); //test friend function, since all term is 0, nothing is printed cout<<"\nnow test friend function, since all term is 0," <<"only one 0 will be printed for R\n"<<R; //test []operator cout<<"\nnow test operator [], try to change order of 3 from 0 to 1\n"; cout<<"before change Q is\n"<<Q; cout<<"\nnow change term 3 to 1\n"; Q[3] = 1; cout<<"\nnow Q is\n"<<Q; //test operator +,-,*,= cout<<"\nnow test operator +,-,*,=\n"; cout<<"R = Q\n"; cout<<"before assignment\n"; cout<<"R is order of 4 with all term equal 0\n"<<R; cout<<"\nQ is\n"<<Q; cout<<"\nnowR=Q and R is\n"<<(R = Q); cout<<"\n R+Q\n"<<(R+Q); cout<<"\n R-Q\n"<<(R-Q); cout<<"\n R*Q\n"<<(R*Q); //now test extreme cases: cout<<"\nnow test extreme cases\n"; cout<<"self-assignment: R=R which is not allowed\n"; cout<<(R =R); cout<<"\nadding/subtracting/multiplying one Polynomial to itself\n"; cout<<"cout<<(R+R)\n"<<(R+R); cout<<"\ncout<<(R*R)\n"<<(R*R); cout<<"\ncout<<(R-R), as all term is 0, only one 0 will be showed," <<(R-R); //add/subtract/multiply/assign two different order Polynomial\n cout<<"\nadd/subtract/multiply/assign two different order Polynomial\n"; Polynomial S(f2, 3); cout<<"Q is:\n"<<Q; cout<<"\nS is:\n"<<S; cout<<"\nP+S: "<<(Q+S); cout<<"\nP-S: "<<(Q-S); cout<<"\nP*S: "<<(Q*S); //test zero order Polynomials cout<<"\ntest zero order Polynomials: T is created by default\n"; Polynomial T; cout<<"So T will be only one 0\n"<<T; //test negative polynomials cout<<"\ntest negative polynomials: Polynomial U(-3);\n"; Polynomial U(-3); //test assignment for an invalid array of constructor; cout<<"\ntest input of invalid array for constructor and it " <<"should display error message\n"; float* invalidArray = NULL; Polynomial V(invalidArray, 4); cout<<"\nand such object will be initialized to order=-1, polyPtr =NULL " <<"so nothing will be displayed\n"<<V; //test constructor with valid array, but negative order cout<<"\ntest constructor with valid array, " <<"but negative order:Polynomial W(f1, -3) \n"; Polynomial W(f1, -3); cout<<"\nand such object will be initialized to order=-1, polyPtr =NULL " <<"so nothing will be displayed\n"<<W; //now test SAFE assignment cout<<"\nnow test SAFE assignment: if an object encounter exception during creation\n" <<"its array will be initialized to NULL pointer, when assigning this object to\n" <<"other object, it will surely fail. However, the assigned object should at least\n " <<"keep its own original data, this is called safe assignment. Now assign Q with\n " <<"this empty object V\n"; cout<<"now V should be completely empty (order is even NOT 0! so nothing should" <<" be displayed at all):\n"<<V<<endl; cout<<"before assignment Q is:\n"<<Q<<endl; cout<<"now assign Q=V which will fail for sure as order of V is -1\n" <<"and an error message will indicate failure of assignment\n"; Q = V; cout<<"\nQ should be the same\n"<<Q<<endl; //test if subscript exceeds limit cout<<"now test if subscript exceeds limit\n"; cout<<"now Q is:\n"<<Q<<endl; cout<<"\nattempt to access Q[9]:\n"; Q[9] = 50; cout<<endl; return 0; }
Running result of program:
test default constructor, and P should be order of 0 with one term initialized to 0:
order of P:0
all element of P is:
0
test constructor with float array as input, now Q should be:
7.6x^5 + 6.5x^4 + 4.3x^2 + 3.2x + 1.3
Test friend function and term of 0 will not
be showed--term of order of 3 is 0 and will not be showed:
7.6x^5 + 6.5x^4 + 4.3x^2 + 3.2x + 1.3
now test copy constructor, R will be initialized same as Q
now Q is
7.6x^5 + 6.5x^4 + 4.3x^2 + 3.2x + 1.3
now R should be same as Q
7.6x^5 + 6.5x^4 + 4.3x^2 + 3.2x + 1.3
now test conversion constructor, R should be initialized to order of 4
by R = 4, and all term is initialized to 0 and R is:
0
now test friend function, since all term is 0,only one 0 will be printed for R
0
now test operator [], try to change order of 3 from 0 to 1
before change Q is
7.6x^5 + 6.5x^4 + 4.3x^2 + 3.2x + 1.3
now change term 3 to 1
now Q is
7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3
now test operator +,-,*,=
R = Q
before assignment
R is order of 4 with all term equal 0
0
Q is
7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3
nowR=Q and R is
7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3
R+Q
15.2x^5 + 13x^4 + 2x^3 + 8.6x^2 + 6.4x + 2.6
R-Q
0
R*Q
57.76x^5 + 42.25x^4 + 1x^3 + 18.49x^2 + 10.24x + 1.69
now test extreme cases
self-assignment: R=R which is not allowed
Self-assignment not allowed
7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3
adding/subtracting/multiplying one Polynomial to itself
cout<<(R+R)
15.2x^5 + 13x^4 + 2x^3 + 8.6x^2 + 6.4x + 2.6
cout<<(R*R)
57.76x^5 + 42.25x^4 + 1x^3 + 18.49x^2 + 10.24x + 1.69
cout<<(R-R), as all term is 0, only one 0 will be showed,0
add/subtract/multiply/assign two different order Polynomial
Q is:
7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3
S is:
5x^3 + 6.7x^2 + 4.5x + 2.3
P+S: 7.6x^5 + 6.5x^4 + 6x^3 + 11x^2 + 7.7x + 3.6
P-S: 7.6x^5 + 6.5x^4 + -4x^3 + -2.4x^2 + -1.3x + -1
P*S: 7.6x^5 + 6.5x^4 + 5x^3 + 28.81x^2 + 14.4x + 2.99
test zero order Polynomials: T is created by default
So T will be only one 0
0
test negative polynomials: Polynomial U(-3);
Order cannot be negative number!
test input of invalid array for constructor and it should display error message
Invalid array input
and such object will be initialized to order=-1, polyPtr =NULL so nothing will be displayed
test constructor with valid array, but negative order:Polynomial W(f1, -3)
Order cannot be negative number!
and such object will be initialized to order=-1, polyPtr =NULL so nothing will be displayed
now test SAFE assignment: if an object encounter exception during creation
its array will be initialized to NULL pointer, when assigning this object to
other object, it will surely fail. However, the assigned object should at least
keep its own original data, this is called safe assignment. Now assign Q with
this empty object V
now V should be completely empty (order is even NOT 0! so nothing should be displayed at all):
before assignment Q is:
7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3
now assign Q=V which will fail for sure as order of V is -1
and an error message will indicate failure of assignment
Order cannot be negative number!
Q should be the same
7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3
now test if subscript exceeds limit
now Q is:
7.6x^5 + 6.5x^4 + 1x^3 + 4.3x^2 + 3.2x + 1.3
attempt to access Q[9]:
Subscipt exceeds order limit!