OOP Basics with C REPRESENTING REALWORLD DATA CLASSES

OOP Basics with C++ REPRESENTING REAL-WORLD DATA, CLASSES AND OBJECTS, DECLARING, INITIALIZING AND USING CLASSES, BASIC C++ CLASSES SPECIFICS

Table of Contents • Special Types – typedefs, enums, unions • Ways of Representing the Real World • Classes and Objects • Using C++ Classes and Objects Defining Classes, Initializing Objects, Methods, Constructors, Allocation • Access Modifiers, Destructors • • OOP Specifics in C++ 2

Special Types in C++ TYPEDEFS, ENUMS, UNIONS

Using typedef • typedef allows you to create an alias of an existing type • Syntax: like declaring a variable of a type, but: • • • Put typedef somewhere in the type specifier sequence What would have been a variable name becomes the type name E. g. : typedef unsigned long ull; here ull is the alias E. g. : unsigned long typedef ull; is equivalent to the above E. g. : typedef string Ten. Strings[10]; Ten. Strings arr; – arr is now a string array with 10 elements • Very useful to shorten long or hard to read type expressions • Avoid overusing typedefs, typedef they can actually make code harder to read 4

typedef LIVE DEMO

C++ Enumerations • Enumerations contain a fixed list of special constant values Have some semantic meaning in the real world • E. g. colors, currencies, date formats, account permission levels, etc. • C++ has two ways to make enumerations – enum and enum class • • Simple enum just defines a list of constant integer numbers • If we have enum color {red, blue, pink}; then color eye. Color = blue; and color eye. Color = 1; are equivalent • enum class in C++11 defines a new data type • enum class Color {red, blue, pink}; Color eye. Color = Color: : blue; 6

C++ Enumerations LIVE DEMO

Unions in C++ (don’t use without a good reason) • Several variables use the same shared data • union Mix { char c; int num; float f; }; Mix mix; mix. c = ‘a’; mix. num = 42; //overwrites mix. c (with 0 in this case) • How it works: Memory initialize for the largest variable type • Each variable effectively points to the start of that memory • • One application is operating on separate bytes of a number • union Integer { int number; char bytes[sizeof(int)]; }; 8

Representing the Real World in Code THE ISSUES WITH USING JUST PRIMITIVE TYPES

Representing the Real World in Code • So far our data types were essentially “just numbers” int, int float and double are obviously numbers • char is also a number, although treated like a symbol • arrays of the above types are still just numerical data • • The physical world CAN be represented entirely by numbers • Computers work with 1 s and 0 s anyway • What matters is not the data itself, but how you interpret it 10

Procedural Programming • So far, we’ve been (mostly) doing Procedural Programming • We only have primitive data types to represent data To represent a complex entity like a person, we make multiple variables: int age; char* name; double height. Meters; • How do we represent multiple people? One solution is parallel arrays: • int ages[num. People]; char* [num. People]; double heights[num. People]; • Actions on that data are done through functions • E. g. to create a person, we could have something like this: void write. Person(int ind, char* name, int age, double height) { names[ind] = name; ages[ind] = age; heights[ind] = height; } 11

Procedural Programming – Issues • Procedural code quickly becomes hard to read • You need to keep track of all variables representing an entity • Functions need to accept a lot of parameters • Hard to extend – multiple edits for each added entity data field • E. g. if we want to add a weight. Kgs to our person, we need to add an array and a parameter to the write function – and any other place we initialize • Hard to read – intention gets lost between implementation details • Compiler doesn’t know which data is together and which isn’t 12

Object-Oriented Programming OOP CONCEPT AND C++ OOP

Object-Oriented Programming • Introduces ways to group data into user-defined data types E. g. a Person type, a Car type, a Linked. List type, etc. • Variables defined in a user-defined type are called “fields” • • A user-defined type is called a “class” • A variables of the user-defined type is called an “object” • The class can own functions, which are part of its definition Such a function we call a “method” • Enable actions directly on the objects and compiler enforces that • • The class can have its own memory allocation and deallocation logic 14

Classes and Objects in C++ • Classes Defined class keyword, followed by a name • Class definition is placed in {} brackets • Definition contains the class “members” – fields, methods, constructors, destructors • Note: streams (istream, istream stringstream) stringstream and string are C++ classes • • Objects – variables of a data type which was defined with class Can be made into arrays. Can be passed into functions (same as primitives) • Note: cout and cin are objects, any string variable you make is an object • Accessing members of an object is done through the. (dot) operator • 15

Using C++ Classes and Objects DEFINING CLASSES, CLASS MEMBERS, INITIALIZING CLASS OBJECTS, ALLOCATION TYPES, DEALLOCATION AND PASSING TO FUNCTIONS

Defining C++ Classes • Syntax: class keyword followed by name you want and {} class Class. Name { access_modifier: members… Don’t forget the ; after access_modifier: … the class definition }; Members inside the brackets are C++ variables and/or function declarations • Access modifiers determine what code has access to the members – we’ll discuss them later in this lecture • 17

Defining C++ Classes – Example • Let’s define a Person class With an age, a name and a height • For now, ignore access modifiers – just place public: at the beginning • • Notice we can use data types which are themselves classes • name here is a string, string which is also a class #include<string> class Person { public: std: : string name; int age; double height. Meters; }; int main() { Person p; } • We can also define a class inside another class Good to do when the inner class is semantically a part of the owning class • Here, we could have a Body in Person and move height. Meters there • 18

Defining C++ Classes LIVE DEMO

Using C++ Objects • We get C++ objects by creating a variable of a class data type • Objects follow the same rules as normal variables Passing them to functions creates a copy of them • Can be passed to functions by reference – the same way as normal variables • Can be made into arrays, can be allocated with new • • Accessing members is done through the. (dot) operator • If accessing through a pointer, the. (dot) operator is replaced by -> • Terminology reminder: variables inside classes are called fields 20

Using C++ Objects – Example • Let’s create a Person object and assign their fields some values • NOTE: using what we know so far, i. e. no constructors yet class Person { class Body { public: double height. Meters; double weight. Kgs; }; public: string name; int age; Body body; }; Person person; person. name = "Lorem"; person. age = 42; person. body. height. Meters = 1. 3; person. body. weight. Kgs = 69; Person * new. Person = new Person(); new. Person->name = "Ipsum"; new. Person->age = 4; new. Person->body. height. Meters = 0. 4; new. Person->body. weight. Kgs = 4. 5; delete new. Person; 21

Using C++ Objects LIVE DEMO

Constructors & “Default” Initializers • A bit annoying to initialize each field, isn’t it? It’s also bad code: Adding a field to the class means finding all initializations and fixing them • Some cases won’t compile – e. g. if there is a reference field • • Constructors are the correct way to initialize objects Special method executed when memory is allocated for the object • Allows the class to specify how the fields are initialized • • There also “default” initializers: Set values to the fields where you declare them (like initializing a variable) • Executed BEFORE the constructor – i. e. constructor overwrites them • 23

C++ Simple Constructors • Defined the same way as a function, but: • No return type & must have the same name as the class • Syntax: Class. Name(parameters) { body } Can have multiple constructors, each with different parameters • Parameters can have default values • • Default constructor – a constructor without parameters Auto-generated ONLY IF no other constructor is defined (does nothing) • Called when no other initialization is provided • Mandatory for default-initialized variables (e. g. an array of variables) • 24

Calling Constructors • Default constructor – called automatically on declaration • • • Example: Person p; string s; default constructor Example: Person p. Arr[3]; default ctor on each element Example: Person * p = new Person(); default ctor Example: Person * p = new Person[3]; default ctor on each element Incorrect: Person p(); is a function declaration, NOT a default ctor call • Constructor with parameters: Class. Name obj(parameters) Example: Person • p(“name”, 42, 1. 82); p = Person(“name”, 42, 1. 82); * p = new Person(“name”, 42, 1. 82); 25

C++ Simple Constructors LIVE DEMO

TIME’S TIME: UP! Quick Quiz • What values will p have for its fields? a) b) c) d) e) name empty, age==0 height==0 name==“Ary O’usure”, O’usure” age==42, age==42 height==1. 3 name empty, age==42 height==1. 3 name==“Ary O’usure”, O’usure” age==0, age==0 height==0 There will be a compilation error class Person { public: string name; int age = 0; double height = 0; Person(string name, int age, int height) { name = name; age = age; height = height; } }; . . . Person p("Ary O'usure", 42, 1. 3); 27

NAMES CONSTRUCTOR PARAMETERS SAME AS FIELDS FOR CLARITY PITFALL: HIDING FIELDS WITH PARAMETERS The parameter names match the field names here. When there is such a conflict, the “more-local” variable hides the “less-local” variable. So, the constructor in this case will assign the parameters with their own values and not see the fields at all. ENDS UP HIDING AND NOT ASSIGNING FIELDS BY FORGETTING TO USE THIS-> 28

The this Pointer • C++ gives us the this pointer to explicitly access class members • this points to whatever the current object is At the point when a method/constructor is called • i. e. it gives you the context in which you are working • • Very useful in any method where parameters match the fields • There is a common convention to always use this, this even if not needed Person(string name, int age, double height) { this->name = name; this->age = age; this->height = height; } 29

Pitfall: Hiding Fields Solution: Using this-> LIVE DEMO

C++ Constructor Initializer List • Constructor body is ALWAYS executed AFTER member initialization • What if members can’t default-construct (have no default constructor)? • Use initializer list – executes before body: Class. Name(parameters) : member 1(member 1 Params), … member. N(member. NParams) { } If a member is omitted, it is default-constructed (if possible) • This syntax is also immune to the member-hiding problem – the compiler knows which is a member and which – a parameter • 31

C++ Constructor Initializer List LIVE DEMO

Methods • Methods are just functions declared inside the class • Compiler knows which methods belong to which class • Methods can access class fields and other members directly Can read and write fields, call other methods, etc. • Can use this-> to explicitly refer to members • • Syntax is the same as for a normal function • Called by using the member-access operator. (dot) on an object • Or the -> operator on a pointer to an object 33

Methods – Example • size(), size() find(), find() replace(), replace() etc. are all methods of string • In most cases, a function which works on an object of a class should be a member of that class • Let’s make the functions we used on Person into Person methods: void print. Person. Info() { cout << "name: " << this->name << ", age: " << this->age << ", height: " << this->body. height. Meters << ", weight: " << this->body. weight. Kgs << endl; } void make. Person. Older(int years) { this->age += years; } 34

C++ Methods LIVE DEMO

Methods – Code Quality Issues of the Last Example • The methods in the previous example weren’t very good: • Should a Person know about and access the console? • This is low cohesion – the class knows more than its name suggests • Should a Person directly access a Body? Body No, that’s not what I meant!. . . But. . if you’re interested… • Bad encapsulation & high coupling – the class has access to implementation details of another class • • Do we need “Person” in method names on a Person class? • They all work on the Person class, we don’t need to write it everywhere 36

Methods – Refactoring for better Quality • This is somewhat better (note: it’s not perfect yet) class Person {. . . void make. Older(int years) { this->age += years; }. . . string get. Info() { ostringstream info; info << "name: " << this->name << ", age: " << this->age << ", " << this->body. get. Info(); return info. str(); } }; class Person { class Body {. . . string get. Info() { ostringstream info; info << "height: " << this->height << ", weight: " << this->weight; return info. str(); } }; . . . }; 37

C++ Refactoring Methods LIVE DEMO

Writing a Smart. Array class • We now know (almost) enough to write a class start to finish • Let’s write a Smart. Array class, which: Allocates memory dynamically (with new) new • Knows it’s size • Has methods to increase or decrease size • Has methods to write and read array elements • • Make it work for int for now. We can worry about making it work for different types later on. • Don’t worry about full support for returning and passing to functions for now. We’ll do that later on. 39

Smart. Array Class – Main Functionality LIVE DEMO

Encapsulation • Consider the code below, say we want to call change. Size() What would happen if external code previously modified current. Size? current. Size • What would happen if external code previously modified data? data • class Smart. Array { public: int * data; int current. Size; Smart. Array() : data(new int[0]), current. Size(0) { }. . . }; class Smart. Array {. . . void change. Size(int new. Size) { int * old. Data = this->data; this->data = new int[new. Size](); for (int i = 0; i < new. Size && i < current. Size; i++) { this->data[i] = old. Data[i]; } this->current. Size = new. Size; delete[] old. Data; } }; 41

C++ Encapsulation – Access Modifiers • Tell the compiler which members can be accessed from what code: public – can be accessed both by code “outside” & “inside” the class • private – can be accessed ONLY by code “inside” the class • There is also protected, protected as well as the friend modifier – we’ll see them in another lecture • • Syntax: write access. Modifier: inside the class definition Members following it will have that access, until another modifier is reached • Can have as many access modifiers as you need in a class • 42

Adding Encapsulation to Smart. Array LIVE DEMO

Encapsulation – Good Habits • Internal class data and operations should usually be private That way the class knows nothing is going to mess with its internal data • The class can do operations on the data expecting it to be in a specific state • Rule of thumb: start with private then change to public only if necessary • • Only members necessary for external usage should be public • Getter method – returns value of a private field • Setter method – writes value to a private field Also checks validity of input data • Also keeps internal object state correct • 44

Encapsulation – Example on the Person class • Make age and weight be private and editable (validate data) • Make name be read-only class Person {. . . private: string name; int age = 0; Body body; public: . . . Person() : body(0, 0) { }. . . class Person {. . . string get. Name() { return this->name; } void make. Heavier(double kgs) { if (kgs < 0) { throw "kgs must be a positive number"; } this->body. increase. Weight(kgs); } }; 45

Getters and Setters LIVE DEMO

const Methods • const parameters mean the parameter won’t be changed Ever wonder how the compiler knows that calling str. size() on a const string& str won’t change str? str • Of course not, you only think about yourself! • • Non-mutating method – a method which doesn’t change the object • C++ allows specifying whether a method is non-mutating Syntax: place const AFTER the method parameter brackets • Example: string get. Name() const { return this->name; } • • Good habit – use const on EVERY non-mutating method 47

const Methods LIVE DEMO

The Smart. Array class – Solving Memory Issues • What happens with int* data after the object goes out of scope? We need to delete it, but how do we know when it goes out of scope? • We need the destructor method – invoked when object goes out of scope • • OK, so we add a destructor which frees the memory • What happens when we return a Smart. Array from a function? • What happens if we have Smart. Array a, b; . . . a = b; ? b; • Code which handles the above correctly follows the “Rule of Three” • We’ll cover theory in detail later, but let’s try to do it in practice now! 49

Implementing the Rule of Three on Smart. Array LIVE DEMO

C++ OOP Specifics • classes class start with private: access, even if you don’t write it • There’s another keyword for making classes – struct Absolutely the same as class, class but starts with public: access by default • The general agreement is to use struct when the class has no methods • • Accessing classes inside classes is done through the : : operator E. g. if our Body class was public and had a public constructor, we could do: Person: : Body body = Person: : Body(1. 82, 69); from main() • The same operator is used for static members – discussed in next lecture • • Class members can be declared inside & defined somewhere else 51

Summary • We saw special C++ types and typedefs • We talked about what OOP is and why it is important • • • We learned how to define classes and create objects We added constructors and methods to our classes We practiced the quality-code concepts encapsulation and cohesion We wrote our own data structure – Smart. Array We briefly clashed with destructors and the Rule of Three • Next time: Code Organization • Namespaces, preprocessors, exceptions, multiple files, more IDEs 52

Questions? 53
- Slides: 53