ECS 110: Data Structures and Programming Discussion Section Notes -- Week 1 Ted Krovetz (tdkrovetz@ucdavis.edu) C++ IN A NUTSHELL ----------------- 1. MOTIVATION The purpose of the first discussion section is to expose you to the basic mechanics of C++. I assume you all know ANSI C well. Almost all C code compiles and runs just as well using a C++ compiler. This is because C++ is designed to improve upon the C language while remaining backwardly compatible and maintaining C's elegance and efficiency. However, the improvements that are in C++ are numerous and taking advantage of them is a good idea and facilitates the study of data structures. Sections 1.4 and 2.1 of the Horowitz book are concise introductions to the parts of C++ that are different from C and you should know for this class. Read these sections carefully. I will illustrate a few of the more important elements from these sections in discussion section this week. 2. OBJECT-ORIENTED PROGRAMMING This course is not a course on object-oriented programming. It is a course on programming using data structures which happens to use an object-oriented programming language. We use C++ because it is a good language for demonstrating and implementing key concepts in the study of data structures, and not because of its many object-oriented features. Thus, we will not be using such C++ facilities as inheritance, templates or built in exception-handling. We will be making heavy use of classes to implement data structures and the algorithms that operate on them. We will also use some parts of C++ which improve upon C in other ways. However, "The central element of object-oriented programming is the encapsulation of an appropriate set of data-types and their operations. The class construct...provides an appropriate tool. Class variables are the objects to be manipulated." (Pohl, 1993). We will be encapsulating and manipulating a great many data-types in this course, and in this sense we will be using C++'s most important object-oriented feature. 3. IMPORTANT C++ DIFFERENCES FROM C Below are the few most important and/or difficult C++ facilities that you should know how and when to use. The numbers in parentheses are the section numbers from the Horowitz book which concisely demonstrate their use. 3.1. (1.4.5) Comments in C++ C style comments are retained, but should only be used for multiline comments. C++ introduces a less error-prone single-line comment. All text after // on a line is ignored by the compiler. Example: // This is a C++ comment. j = ++i; // This is a comment, too. 3.2. (1.4.6) Input/Output in C++ To perform I/O in C++ it is necessary to include the system-defined header . The keyword cout is used for output to the standard output device. The cout keyword and each entity being printed are separated by the << operator. The entities being output are printed in left to right order on the standard output device. Use the endl token to signify an end of line to be output. Likewise, the cin keyword is used for input in C++, and the >> is used to separate variable being input. Whitespace (i.e. the tab, newline, or blank characters) is used to separate items corresponding to different variables on the standard input device. The following example reads two integers, multiplies them, and writes the result. #include int main() { int in_A, in_B, out_C; cin >> in_A >> in_B; out_C = in_A * in_B; cout << out_C << endl; return 0; // Modern C++ compiles expect main() to return 0 to indicate success } 3.3 (1.4.11) Dynamic Memory Allocation in C++ Two new built-in operators in C++ are used to allocate and release memory from free store. The new operator creates an object of a desired type and return a pointer to it. Special syntax is used when creating an array. Examples: int* i,j; // Declare variables i and j, which will point to integers i = new int; // The type to be created follows the new operator j = new int[10] // j now points to an array of 10 ints The delete operator disposes of memory previously created by new. There is a special syntax for releasing an array. Examples: delete i; // i is no longer a valid pointer delete [] j; // j is no longer a valid pointer 3.4 (2.1.1) An introduction to the C++ Class C++ introduces the class keyword which behaves much like C's struct but allows functions to be declared inside the class declaration and provides access control to all data members and function members. Members defined as public can be accessed by any function, regardless of class membership. Members defined as private can generally be accessed only by functions which belong to the class. Example: class Point { public: int GetX(); void SetX(int); int GetY(); void SetY(int); private: int x, y; }; int Point::GetX() { return x; } int main() { Point p; p.x = 5; // ILLEGAL!! x is a private data member p.SetX(5); // Much better return 0; } 3.5 (2.1.4) Special Class Operators (Constructors and Destructors) You may optionally declare two special functions in your classes; a constructor and a destructor. The constructor is automatically invoked once whenever a variable of a particular class is created (either statically or dynamically), and the destructor is automatically invoked once whenever a variable of a particular class is destroyed. The syntax differs from other member functions. Example: class Point { public: Point(); // Constructor declaration ~Point(); // Destructor declaration int GetX(); void SetX(int); int GetY(); void SetY(int); private: int x, y; }; Point::Point() { x = 0; y = 0; // A good place to initialize data members. } Point::~Point() { // A good place to deallocate any pointers allocated by the constructor. } 3.6 (2.1.4) Special Class Operators (Operator Overloading) C++ allows you to alter the normal function of an operator. For instance a well designed and implemented Point class would override the '+' operator to allow it to work with Points in a meaningful and intuitive way. Example: main() { Point a, b, c; c = a + b; // This is more natural } You will want to take advantage of this expressive power in the future, and I will give examples in later sections. In the meantime, you must at least minimally learn and use operator overloading in your current assignment to print a class to standard output. To do so, you must overload the << operator in ostream.h. An example is given in the sample program at the end of these notes. Note in the example that the overloaded function must be declared as a 'friend' function in the class definition. Friend functions are allowed access to members defined as private even though they are not themselves members of the class. This breaks down the data abstraction and data hiding benefits of C++ and should be avoided whenever possible. The << operator must have access to the private members to print them, and thus must be declared friend. 4. EXAMPLE PROGRAM If time permits, the following program will be constructed during discussion section. Regardless, you should examine the program to see how classes are defined and used. Running the example on the system you intend to use would be good introductory practice, too. #include const int max_length = 100; class Stack { public: Stack(); ~Stack(); void Push(int); void Pop(); int Top(); int Empty(); friend ostream& operator<< (ostream& os, Stack& s); private: int* storage; int top; }; Stack::Stack() { cout << "Constructing" << endl; storage = new int[max_length]; top = -1; } Stack::~Stack() { cout << "Deconstructing" << endl; delete []storage; } void Stack::Push(int new_value) { storage[++top] = new_value; } void Stack::Pop() { top--; } int Stack::Top() { return (storage[top]); } int Stack::Empty() { return (top == -1); } ostream& operator<< (ostream& os, Stack& s) { if (s.Empty()) os << "The stack is empty"; else for (int i = s.top; i >= 0; i--) os << s.storage[i] << " "; os << endl; return os; } int main() { Stack s; int num; cout << "How many would you like to push? "; cin >> num; for (int i = 1; i <= num; i++) { s.Push(i); cout << s; } while ( ! s.Empty()) { cout << "The top of the stack is " << s.Top() << endl; s.Pop(); } cout << s; return 0; }