/* ** $Id: counters.cc 841 2008-03-28 01:16:23Z phf $ ** ** Here's a first full example of a C++ class. We'll see ** many more... :-) ** ** Here we implement a simple "counter" that starts at 0 ** and is given an upper limit when it is created. We can ** tick() the counter up one by one until that limit is ** reached. We use assert() in several places to make sure ** that certain preconditions are fulfilled; not always the ** best choice, but since we didn't talk about exceptions ** yet, this is okay. */ #include #include // Here's the class, we will see later how to move this into // a separate file that can be compiled independently. class Counter { // We put an underscore in front of these so we // can use the names without underscores as names // for methods; this is one of several conventions // you'll find in C++ code. Note that these member // variables are private (by default). int _value; int _limit; public: // The constructor for counters. Essentially this // method defines how counter objects are created. // Since we want counters with a limit, we have to // pass it in here. // // Note that the constructor can also be called by // C++ automatically, as an "implicit conversion". // We'll illustrate that below with a function... // // Finally, notice the strange syntax we use to // initialize the member variables; I require you // to use -Weffc++ which doesn't like it when you // initialize things in the constructor code; it // wants you do use that "member initialization // list" thing instead. We'll see later why that // is a good idea. (The code that you would have // written in Java is still below, but just for // purposes of this example, it could go away.) Counter(int limit): _value(0), _limit(limit) { // Make sure the limit is valid. assert(limit > 0); // Remember that this counter has this limit. // this->_limit = limit; // Initialize the current value of this counter. // this->_value = 0; } // The method to increment the counter. This only // works if the current value is below the limit // for this counter. void tick() { // Ensure we have not reached the limit yet. assert( this->_value < this->_limit ); // Increment the current value of this counter. this->_value += 1; } // Get the current value for this counter. int value() { return this->_value; } // Get the limit for this counter. int limit() { return this->_limit; } }; // class Counter // Function to illustrate implicit conversions. void counter_conversion(Counter c) { std::cout << "the power of conversion " << c.value() << " " << c.limit() << std::endl; } // A short main program, showing how the counters work. int main() { // First we declare an object using static allocation // (on the stack). Note that you can't do this in Java // at all, there all objects much be created using the // "new" operator (on the heap). Counter c(10); // Note how similar the above is to declaring a simple // integer. Interesting aside to think about: int a = 10; int b(10); std::cout << a << std::endl << b << std::endl; // The first is the same in C, the second is new in C++ // (unless I am drunk?). // Anyway, here's how to use the counter object: for (int i = 0; i < 5; i++) { std::cout << "Value: " << c.value() << " Limit: " << c.limit() << std::endl; c.tick(); } // If you want to be closer to Java, you can also // create the object using dynamic allocation (on // the heap). In C++ you then have to deal with the // fact that your reference to the object is actually // a pointer. Counter *x = new Counter(10); // Here's how to use this counter object: for (int i = 0; i < 5; i++) { std::cout << "Value: " << x->value() << " Limit: " << x->limit() << std::endl; x->tick(); } // Free the counter object again, not strictly necessary // here but we'll do it anyway. delete x; // One more thing: There's a function counter_conversion // above that takes a Counter as its sole argument. Now // we'll call it, but with an integer... counter_conversion(12); // Did you see that? We defined a constructor for Counter // that takes an integer. In the call above, C++ has the // integer 12 and needs to pass it to a counter. Well... // C++ is simply going to "make up" a counter object for // you that will be initialized with the 12! This may // seem convenient now, but trust me, it can get very // confusing. Marking the constructor "explicit" would // avoid this behavior and result in an error message // instead. // We're all done! :-) std::cout << "Woohoo!" << std::endl; }