For instance, if we created a List class, it might include all the information and routines needed to use a list of items. We could then declare and use individual list variables, or objects.
In fact, classes go further than this by also providing mechanisms to limit what parts of the class are accessible by other parts of the program, by creating routines which run automatically when an object is created or destroyed, and by allowing one class to be based on another, automatically inheriting all the properties and functionalities of the original and expanding on them.
In C++, the syntax for classes is based on the struct syntax. For example, to declare a List class we could use
class List { // listing of the class data fields and routines would go here };To declare actual list variables (each one presumably capable of storing and handling its own list of items) we would use the syntax:
List L; // a List variable named L List anotherone; // and another list variable List mylist; // and another
class List { int listItems[100]; // array for storing the list items int currentItems; // track how many items are stored right now };We can access the fields using the same syntax as with structs, e.g.
List L1, L2; // L1 and L2 are both Lists L1.listItems[0] = 10; // put value 10 in the first spot in L1's array L1.currentItems = 1; // update how many items L1 holds // put two items in L2 and update its currentItems field L2.listItems[0] = 3; L2.listItems[1] = 7; L2.currentItems = 2;
Each method is given a prototype in the class definition, e.g.
class List { // data fields int listItems[100]; // array for storing the list items int currentItems; // track how many items are stored right now // operations to manipulate the list bool insert(int i); // routine to insert value i into the list // and return true if successful, false otherwise bool remove(int v); // routine to search the list for value v // and remove it, returning true iff successful };As with data fields, we apply an operation to a specific list by specifying which list variable and which method, e.g.
List L; // try to insert value 1 into list L, // and print a message based on whether // the insert routine returned true or false if (L.insert(1)) { cout << "inserted value 1 successfully" << endl; } else { cout << "something when wrong when trying to insert value 1" << endl; }The syntax for implementing the routines needs to specify the name of the class and the name of the method being implemented, so for the two routines above the implementations might look something like the example below.
(Note that each method in the class automatically has access to all its data fields.)
// define the class itself class List { // data fields int listItems[100]; // array for storing the list items int currentItems; // track how many items are stored right now // operations to manipulate the list bool insert(int i); // routine to insert value i into the list // and return true if successful, false otherwise bool remove(int v); // routine to search the list for value v // and remove it, returning true iff successful }; // then we can provide full implementations of the routines // first the insert routine for the List class bool List::insert(int i) { if (currentItems >= 100) { // the list is already full, so we cannot insert return false; } else { // insert value i in the next available spot and // increment the count of the number of stored items listItems[currentItems] = i; currentItems++; return true; } } // next the remove routine for the List class bool List::remove(int v) { // if there is nothing in the list return false if (currentItems < 1) { return false; } // otherwise search for the first spot containing v int p = 0; while ((p < currentItems) && (listItems[p] != v)) { p++; } // if we never found v then simply return false if (p >= currentItems) { return false; } // otherwise 'remove' v by moving the last item // in the list into v's spot // and then decrementing the number of items // stored in the list currentItems--; listItems[p] = listItems[currentItems]; return true; }
A constructor is a routine that is automatically run when a class variable (object) is first declared, and a destructor is a routine that is automatically run when a class variable is destroyed/deleted.
In the case of C++, a constructor always has the same name as the class itself, and a destructor has the same name but with the ~ symbol tacked on before hand. Constructors and destructors never have return types.
class List { // data fields int listItems[100]; // array for storing the list items int currentItems; // track how many items are stored right now // operations to manipulate the list List(); // constructor to initialize the list ~List(); // destructor to do any necessary cleanup on // termination of the list bool insert(int i); // routine to insert value i into the list // and return true if successful, false otherwise bool remove(int v); // routine to search the list for value v // and remove it, returning true iff successful }; // constructor List::List() { // automatically initialize the number of stored items to 0 // when a list is first created currentItems = 0; } // destructor List::~List() { // doesn't really need to do anything in this example }
Public fields/methods can be accessed from any part of the program that has access to the object itself, but private fields/methods can only be accessed from inside the methods of that class. (We'll discuss protected fields/methods when we discuss inheritance.)
Typically we make the data fields private, so that only the class methods can directly access the data fields (ensuring that the only way the data content is adjusted is through our carefully designed and debugged class methods).
class List { private: // data fields int listItems[100]; // array for storing the list items int currentItems; // track how many items are stored right now public: // operations to manipulate the list List(); // constructor to initialize the list ~List(); // destructor to do any necessary cleanup on // termination of the list bool insert(int i); // routine to insert value i into the list // and return true if successful, false otherwise bool remove(int v); // routine to search the list for value v // and remove it, returning true iff successful };In the example above, the list methods (constructor, destructor, insert and remove) are the only things that can access currentItems and listItems, but any part of the program can call the methods themselves.
int main() { List L; // note the constructor automatically runs here on L // try a couple of inserts bool result = L.insert(1); result = L.insert(17); result = L.insert(7); // try a remove result = L.remove(17); return 0; // note the destructor automatically runs here on L }
// the class definition #include <iostream> #include <sstream> #include <string> using namespace std; // class to store and manipulate prices as a combination of // dollars and cents (100 cents per dollar) class price { public: // constructor to initialize price to 0.00 price(); // constructor to initialixe price to d.cc price(long d, long cc); // destructor // (currently displays price just to show when it runs) ~price(); // return price as string, e.g. "$5.99" string priceString(); // copy dollars to d and cents to c void get(long &d, long &c); // set dollars to d and cents to c void set(long d, long c); // add d to dollars and c to cents, // then fix so cents are in range 0..99 void adjust(long d, long c); // multiply dollars and cents by n, // then fix so cents are in range 0..99 void multiply(long n); private: // store dollars and cents seperately long dollars, cents; // method to correct cents to range 0..99 void fix(); }; |
// a sample main routine using the class int main() { // create a price using the default constructor // and display the price as a formatted string price p1; cout << p1.priceString() << endl; // set the price to 1 dollar, 25 cents // and display the price as a formatted string p1.set(1, 25); string s; s = p1.priceString(); cout << "formatted price after set(1,25): " << s << endl; cout << endl; // set the price to 5 dollars and 99 cents using the // alternate constructor price p2(5, 99); // copy the dollar and cents values using the get method // then display them long d, c; p2.get(d, c); cout << "price(" << d << "," << c << "): " << p2.priceString() << endl; // multiply the price by 2 then display it p2.multiply(2); cout << "multiplied by 2: " << p2.priceString() << endl; cout << endl; // use new to dynamically allocate a price and // store its address in a pointer price *p3; p3 = new price(3, 75); if (p3 != NULL) { // if new succeeded display the price cout << "price(3, 75) formatted: " << p3->priceString() << endl; // add 1 dollar and 50 cents to the price // then display the result p3->adjust(1, 50); cout << "after adding 1.50: " << p3->priceString() << endl; // run the destructor on the price delete p3; cout << endl; } // at the end of main the destructors for p1 and p2 are // run automatically (since they are local to the main // routine, and the main routine is now over) return 0; } |
// resulting output $0.00 formatted price after set(1,25): $1.25 price(5,99): $5.99 multiplied by 2: $11.98 price(3, 75) formatted: $3.75 after adding 1.50: $5.25 destructor for $5.25 destructor for $11.98 destructor for $1.25 |
// implementation of the class methods // sets dollars and cents to 0 price::price() { dollars = 0; cents = 0; } // set dollars to d and cents to c and // correct so that cents is in range 0.99 price::price(long d, long c) { dollars = d; cents = c; fix(); } // just here to show when destructor runs price::~price() { cout << "destructor for " << priceString() << endl; } // correct dollars/cents so that cents is in range 0.99 void price::fix() { if (cents > 99) { dollars += cents/100; cents = cents%100; } else if (cents < 0) { dollars -= cents/100; cents = 100 - cents; } } // return string in format $x.yy // where x is dollars and yy is cents string price::priceString() { ostringstream result; if (dollars < 0) result << "-$" << (-dollars) << "."; else result << "$" << (dollars) << "."; if (cents < 10) result << "0"; result << cents; return result.str(); } // copy out dollars and cents void price::get(long &d, long &c) { d = dollars; c = cents; } // set dollars to d, cents to c and // correct so that cents is in range 0.99 void price::set(long d, long c) { dollars = d; cents = c; fix(); } // add d to dollars, c to cents // (either/both can be positive/negative) // and correct so that cents is in range 0.99 void price::adjust(long d, long c) { dollars += d; cents += c; fix(); } // multiply both dollars and cents by n and // correct so that cents is in range 0.99 void price::multiply(long n) { dollars *= n; cents *= n; fix(); } |