CSC 324 Tutorial Week 8 ======================= Introduction to C++ =================== Everyone starts with the "Hello world" program. Fine. helloworld.cc: #include using namespace std; int main() { cout << "Hello world" << endl; return 0; } We save C++ (like many languages) is compiled. The standard (free) compiler is called g++ (Gnu C++ Compiler). g++ -Wall -ansi -pedantic helloworld.cc The -Wall -ansi -pedantic switches turn on all compiler warnings and uses the strict syntax--usually very good ideas! The program is written as a.out. (We can choose the output file by adding "-o helloworld".) Let's take a look at program a little more closely, line-by-line. #include //include the definitions for standard stream input/output using namespace std; //use the standard namespace (we always will) //namespaces are like Java packages int main() //main function is the entry point to our program { cout << "Hello world" << endl; //cout is streaming output to console (stdout) //The << operator is overloaded for streams. //endl is a newline, '\n'. //cout< using namespace std; int main() { int num; while (cin >> num) { // "cin>>var" returns a bool (Boolean) for whether // there was more data to read cout << num << endl; // << is overloaded to print any basic datatype } return 0; } We're not sure how many numbers we'll be reading, so let's use a dynamic array (called a vector in C++ and other languages/libraries). We need to tell vector what datatype to store. Here we want ints, so we declare: vector nums; The angle brackets < > say how to instantiate vector (which is templated). We can add elements to a vector using the push_back method. Let's store those numbers we read. #include #include // need to include vector definitions using namespace std; int main() { vector nums; int num; while (cin >> num) { nums.push_back(num); /// vector has method /// public void push_back(T e) /// (argument is actually const T& e) } // and let's compute the average /// we still need to write the sum function cout << "Average: " << sum(nums) / nums.size() << endl; return 0; } Still need to write the sum function. What type should its argument be? - Takes our vector, so basic type is vector. - We won't change it, so we declare it const (so we can sum consts too). - By default, everything in C++ is passed by value, so we'd have to make a copy of the vector. C++ supports pass-by-reference (which is basically passing a pointer to the object), so let's do that with &. So here's our sum function: int sum(const vector& v) { int total = 0; /// One of vector's elements is a typedef /// Accessed with :: (the scope operator) /// const_iterator is an iterator type that promises /// not to modify elements /// begin() is like Java's iterator() /// instead of hasNext(), we compare with v.end(), an /// iterator pointing to the end. /// instead of next(), increment with ++ (overloaded) /// and extract with * (overloaded) /// C++ likes to mimic indexing. for (vector::const_iterator i = v.begin(); i != v.end(); ++i) { /// No casting. vector's iterators produce Ts. total += *i; } return total; } Our program is complete. We may compile and run it. Exercise: template our sum function. (Answer: add "template " before function declaration and replace "int" with "T" throughout. Writing a specialized function first then templating it is a good strategy!) Writing a class --------------- Let's write a class in C++ that implements rational numbers. What do we need for this class? - a numerator n (an int) - a denominator d (another int) - an increment operator (overload +=) - a way to print a rational number (streaming output with cout) Classes are defined similarly in C++ as in Java (there are some differences, i.e., due to the type systems and memory management). class Rational { private: // the private data members and methods int n; int d; public: // the public interface /// class constructor method: /// takes two ints and initializes the newly create class instance Rational(int n, int d) { // an implicit this pointer allows us to access the data members this->n = n; this->d = d; } /// Let's overload the constructors, letting us initialize a Rational /// only specifying the numerator Rational(int n) { this->n = n; d = 1; // d refers to the class data member, as no local d shadows it } /// Define an increment operator: /// allow us to write "r1 += r2;" where r1, r2 are Rationals Rational& operator+=(const Rational& r) { n = n*r.d + r.n*d; d *= r.d; return *this; } /// Define an output function. Can't be part of the class, since it /// is called on objects like cout. Must access private data members /// though. C++ Solution: make it a friend function. /// (Note that cout is derived from class ostream.) friend ostream& operator<< (ostream& o, const Rational& r); }; // Note the semicolon on the end of the class definition!!! // Missing it is the source of many errors! // Here's our output function ostream& operator<<(ostream& o, const Rational& r) { return o << r.n << "/" << r.d; } Let's write the main function so we can see if this code works: int main() { Rational r(1,2); Rational r2(3); r += r2; cout << r << endl; return 0; } This code prints 7/2, exactly what we want. Templated classes ----------------- But suppose we want to be able to store higher precision Rational numbers too (where we use long ints instead of ints). Solution? Revise our class to always use longs? No, let's template the class. Templating a class is just like templating a function. In this case, we put "template " before the class definition, and replace each int with T (and fix up any details). Here's our templated version of the class (and operator<< function): template class Rational { public: Rational(T n) { this->n = n; d = 1; } Rational(T n, T d) { this->n = n; this->d = d; } void operator+=(const Rational& r) { n = n * r.d + r.n * d; d *= r.d; } private: T n; T d; friend ostream& operator<< (ostream& o, const Rational& r); }; template ostream& operator<<(ostream& o, const Rational& r) { o << r.n << "/" << r.d; return o; } We can create a Rational using ints by writing something like Rational r1(1,2); and create a Rational using longs by Rational r2(-1,6); Summing it up ------------- Remember our (templated) sum function? Let's sum up a vector of long Rationals. template T sum(const vector& v) { T total(0); for (typename vector::const_iterator i = v.begin(); i != v.end(); ++i) { total += *i; } return total; } int main() { vector > rs; rs.push_back(Rational(1, 2)); rs.push_back(Rational(2, 3)); rs.push_back(Rational(-1, 6)); cout << sum(rs) << endl; return 0; }