Introduction to Computer Science PDF Fall 2024
Document Details
Uploaded by FervidDune
ETH Zurich
2024
Manuela Fischer and Felix Friedrich
Tags
Summary
Introduction to Computer Science, Fall 2024, course materials. This document provides an introduction to computer science concepts, including information hiding and encapsulation, using C++ examples. The document also introduces classes and constructors in detail.
Full Transcript
INTRODUCTION TO COMPUTER SCIENCE 252-0032, 252-0047, 252-0058 Document authors: Manuela Fischer and Felix Friedrich Department of Computer Science, ETH Zurich Fall 2024 1 Information Hiding...
INTRODUCTION TO COMPUTER SCIENCE 252-0032, 252-0047, 252-0058 Document authors: Manuela Fischer and Felix Friedrich Department of Computer Science, ETH Zurich Fall 2024 1 Information Hiding 157 Section 13 Information Hiding A new type offering functionality like struct rational should be stored in a library. As before for functions, we can distinguish between declaration and definition and go from one single file to two separate files, the header file rational.h shown in Code 13.1 containing only declarations, and the implementation file rational.cpp shown in Code 13.2 containing the corresponding definitions. struct rational { int n; int d; // INV: d != 0 }; // POST: return value is the sum of a and b rational operator+ (rational a, rational b); // POST: return value is the difference of a and b rational operator- (rational a, rational b);... Code 13.1: The header file rational.h, containing the declaration of a struct and all declarations of functions and operators on that struct. #include "rational.h" // include declarations // POST: return value is the sum of a and b rational operator+ (rational a, rational b) { rational result; result.n = a.n * b.d + a.d * b.n; result.d = a.d * b.d; return result; } // POST: return value is the difference of a and b rational operator- (rational a, rational b){... }... Code 13.2: The implementation file rational.cpp containing the definition of operators and all functions This separation of declaration and definition makes the header file more readable and to some extent separates concerns: a user reading the header file does not need to know how the functions are implemented. Information Hiding 158 Subsection 13.1 Encapsulation Programming is, as it will turn out, not only about offering functionality but also about about making good design decisions that need to be defended. Invariants and representation Usually we want to guarantee that invariants hold for a data structure. For example, for each Rational r it must always holds that r.d ̸= 0. Maybe for each Rational r we want to make sure it is always stored in a reduced form. The user should be provided with the “what” and not the “how”. For example, vectors offer functionality such as v.size(), v[i] or v.push_back(j) and we do not need to know how this is realized behind the scenes in order to use the type. It should be possible to change the internal representation without having to rewrite user code. For example, changing from a polar coordinate representation to cartesion coor- dinates for storing complex numbers. Idea of encapsulation (information hiding principle) A type is uniquely defined by its possible values and its functionality. The rep- resentation should not be accessible. Not representation but functionality is offered! The information hiding principle is offered in C++ with classes. Subsection 13.2 Classes Classes provide the concept for encapsulation in C++, are a variant of structs and are provided in many object oriented programming languages. class rational { int n; int d; // INV: d != 0 }; In C++, it can be controlled if members are usable outside a structs or classes with modifiers public and private52. So, the only difference53 between classes and structs is that for a struct by default nothing is hidden while for a class by default everything is hidden. 52 There is one more, protected, that we do not need for this course 53 apart from a further minor difference when it comes to inheritance Information Hiding 159 So indeed the following are equivalent struct rational { class rational { private: int n; int n; ⇐⇒ int d; int d; } } and also class rational { struct rational { public: int n; ⇐⇒ int n; int d; int d; } } In fact, members are not accessible outside the class: Example 13.1 (Unaccessible members) class rational { int n; int d; };... rational r; r.n = 1; // error: n is private r.d = 2; // error: d is private int i = r.n; // error: n is private That is weird. What is the use of hiding something when I cannot make use of it any more? We need another concept. Subsection 13.3 Member Functions Member functions are like members of a class and have access to the hidden data. Member functions can be made public. Information Hiding 160 Example 13.2 (rational with member functions) class rational { public: // POST: return value is the numerator of this instance int numerator () const { member function return n; } // POST set numerator of this instance to value void set_numerator(int value) { n = value; } public area // POST: return value is the denominator of this instance int denominator () const { member functions have access to return d; private data } // PRE value != 0 // POST set denominator of this instance to value void set_denominator(int value) { assert(value != 0); d = value; } private: int n; the scope of members in a class is the int d; // INV: d!= 0 whole class, independent of the decla- }; ration order So, now we have access to the numerator and denominator of a rational via member- functions. Note how the invariant that the denominator is not zero is checked in member function set_denominator. Users cannot violate the invariant (apart from initialization – we will address this below). Member functions are accessed like members: Example 13.3 (Accessing member functions) rational r; member access r.set_denominator(1); int n = r.numerator(); int d = r.denominator(); For beginners it is usually confusing that functionality is introduced that does not seem to add anything useful. Indeed, using the rational number has become more cumbersome than Information Hiding 161 before at this point. If you find this confusing, go on reading and for the time being accept our goal to defend the invariant of the class, namely that the denominator must not be zero. You certainly agree that it was not possible with the struct before to defend this. We are at least closer to this goal now. If you have carefully looked at class rational above, you might have spotted the const keyword after the member function declaration class rational {... int numerator () const { return n; }... This const is to promise that an instance cannot be changed via this function: const objects can only call const member functions: rational x; x.set_numerator(10); // ok; const rational y = x; int n = y.numerator(); // ok; y.set_numerator(10); // error; Member functions When a member function is called, such as in r.numerator() then it is called on an expression with a value of class type. Here, the expression is r and has the value of the instance r. How does the member function access r? In fact, r is passed as an implicit argument. In the class, the corresponding parameter is called (and accessible as) this and is a pointer54 to the class object (here: to r). The const of the member function refers to the instance this and promises that the value associated with this implicit parameter cannot be changed. n in the member function is actually a shortcut for this->n55. It is not necessary to fully understand all the details behind the scenes. And if you feel you have understood how to use the member functions then this is already enough to understand later topics. However, if you want to get an intuition of how member functions might be implemented behind the scenes, consider Figure 13.1 54 will be covered a bit later 55 again, related to pointers and will be covered a bit later Information Hiding 162 class rational { struct fraction { int n; int n;...... public: }; int numerator () const { int numerator (const fraction& that) return this->n; { } return that.n; }; } rational r; fraction r;..... std::cout name >> score) { if (50 score) { if (50