OOP Search

Week 03 / 14 Object-Oriented Programming

Introduction to classes

From struct to class. How to define a class with attributes and methods, which kinds of methods exist, how objects are created, and how public and private protect an object's state.

Objectives
  • Compare struct, union and class
  • Define a class with attributes and methods
  • Define a method inside the class and outside it with the scope operator `::`
  • Tell the kinds of methods apart (access, processing, helper, const)
  • Create objects and explain that each object has its own attributes
  • Use public, private and protected, and know the default access in struct and class

Why a struct is not enough

In week 2 we grouped data with struct. The lab file from week 3 does exactly that:

L03/s2.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstring>
using namespace std;

struct Student
{
	int age;
	string name;
	int* grades;
};

int main()
{
	// ex1: in: input a word 
	// out: and we have to take out all the vowels
	//char s[20], res[20];
	//cout << "Input a word: ";
	//cin.getline(s, 20);

	//int j = 0;
	//for (int i = 0; i < strlen(s); i++)
	//{
	//	if (s[i] != 'a' && s[i] != 'e' && s[i] != 'i' && s[i] != 'o' && s[i] != 'u' && s[i] != 'A' && s[i] != 'E' && s[i] != 'I' && s[i] != 'O' && s[i] != 'U')
	//	{
	//		res[j] = s[i];		
	//		j++;
	//	}
	//}

	//res[j] = '\0';

	//cout << "The resulted word without voewls is: " << res << endl;

	// ex 2: 
	// in: insert a phrase
	// out: count the words
	//char phrase[100];
	//cout << "Insert a phrase: ";
	//int i = 0, countWords = 0;
	//char* p; 
	//char* context = NULL;
	//cin.getline(phrase, 100);
	//p = strtok_s(phrase, " ", &context);
	//while (p != NULL)
	//{
	//	countWords++;
	//	p = strtok_s(NULL, " ", &context);
	//}

	// orrrr not really ok, check at home!!
	//while (phrase[i] != '\0')
	//{
	//	if (phrase[i] == ' ' && phrase[i + 1] != ' ' && phrase[i + 1] != '\0' && phrase[i - 1] != ' ')
	//	{
	//		countWords++;
	//	}
	//	i++;
	//}
	
	//cout << "The number of words within the phrase is: " << countWords << endl;

	// structs
	Student stud;
	stud.name = "Marcel";
	stud.age = 20;
	stud.grades = new int[5] {10, 8, 9, 10, 6};

	// next class

	return 0;
}

Anyone can write anything into stud.age or stud.grades. Nothing stops this:

stud.age = -40;          // compiles, and now the data is nonsense

A struct groups data. It does not protect it. A class groups data and the operations that are allowed on it, and it can hide the data so that only those operations can change it.

struct, union, class

MembersDefault accessTypical use
structdata and functions, each with its own memorypublicsimple groups of data
classdata and functions, each with its own memoryprivatetypes with rules
unionall data members share the same memorypublicone value out of several types

In a union only one member is valid at a time: the last one you wrote.

union Value {
    int i;
    double d;
};

Value v;
v.i = 42;      // i is the active member
v.d = 3.5;     // now d is active; reading v.i from here on is wrong

Reading a member other than the last one written is undefined behaviour in C++. You will rarely need union in this course. It is here so you know what it is.

Defining a class

A class has attributes (data members, the state) and methods (member functions, the behaviour).

class Account {
    double balance = 0;                 // attribute (private by default)
public:
    void deposit(double amount) {       // method defined inside the class
        if (amount > 0) balance += amount;
    }
    bool withdraw(double amount);       // only declared here
    double getBalance() const { return balance; }
};                                      // don't forget the ';'

bool Account::withdraw(double amount) { // defined outside, with ::
    if (amount <= 0 || amount > balance) return false;
    balance -= amount;
    return true;
}

Account::withdraw reads “the withdraw that belongs to Account”. Without Account:: it would be a free function that knows nothing about balance.

bool prints as 1 or 0. What does the program print?

#include <iostream>
using namespace std;
class Account {
    double balance = 0;
public:
    void deposit(double amount) { if (amount > 0) balance += amount; }
    bool withdraw(double amount);
    double getBalance() const { return balance; }
};
bool Account::withdraw(double amount) {
    if (amount <= 0 || amount > balance) return false;
    balance -= amount;
    return true;
}
int main() {
    Account acc;
    acc.deposit(100);
    acc.deposit(-50);
    bool ok1 = acc.withdraw(30);
    bool ok2 = acc.withdraw(500);
    cout << ok1 << " " << ok2 << " " << acc.getBalance();
}

Kinds of methods

  • Access methods read or change one attribute in a controlled way: getBalance(). A setter, if you have one, validates what it receives.
  • Processing methods do the real work of the object: deposit, withdraw. They are the reason the class exists.
  • Helper methods are private. They are used by other methods and are not part of the public interface.
  • const methods promise not to change the object: double getBalance() const. The compiler checks the promise.
  • Constructors and the destructor are special methods. They are next week’s topic.

A method can also be inline (defined in the class body) or defined outside with ::. The behaviour is the same. Long methods usually go outside, so the class body stays readable.

Objects

A class is a type. An object is a variable of that type. Each object has its own copy of the attributes. The methods exist once and work on the object they are called on.

#include <iostream>
using namespace std;
class Counter {
    int value = 0;
public:
    void increment() { value++; }
    int get() const { return value; }
};
int main() {
    Counter a, b;
    a.increment();
    a.increment();
    b.increment();
    cout << a.get() << " " << b.get();
}

Inside a method, this is a pointer to the current object. value++ in a.increment() really means this->value++, with this == &a.

Objects live where any variable lives: local objects on the stack, objects created with new on the heap.

STACK
a.value: 0
b.value: 0
HEAP

Counter a, b; - two objects on the stack, each with its own value.

Step 1/4

Access specifiers

SpecifierWho can use the member
publiceveryone
privateonly the methods of the class (and its friends, week 6)
protectedthe class and the classes derived from it (week 8)

Access is checked by the compiler. This is deliberately broken code:

Account acc;
acc.balance = 1000000;   // compile error: 'double Account::balance' is private within this context

Encapsulation means: the attributes are private, and the public methods keep the object valid. A rule that must always hold for an object, such as “the balance is never negative”, is called an invariant. If only the methods can change the state, you only need to check the invariant in the methods.

Run the program below. Then uncomment the last line and read the compiler message.

#include <iostream>
using namespace std;

struct PointS {        // struct: members are public by default
  int x = 0, y = 0;
};

class Rectangle {      // class: members are private by default
  double width = 1, height = 1;
public:
  bool setSize(double w, double h) {
      if (w <= 0 || h <= 0) return false;   // keep the rectangle valid
      width = w;
      height = h;
      return true;
  }
  double area() const { return width * height; }
};

int main() {
  PointS p;
  p.x = 3;                       // allowed: public
  cout << "point: " << p.x << " " << p.y << endl;

  Rectangle r;
  cout << "area: " << r.area() << endl;
  r.setSize(2, 5);
  cout << "area: " << r.area() << endl;
  cout << "accepted: " << r.setSize(-1, 4) << endl;
  cout << "area: " << r.area() << endl;
  // r.width = -3;               // try it: compile error, width is private
}
Input (stdin)

Open in Godbolt

Check yourself

1. What is the default access of members declared without a specifier?

2. balance is a private attribute of Account. What happens with acc.balance = 5; written in main?

3. Inside double getBalance() const, which of these are allowed?

4. What does the program print?
#include <iostream>
using namespace std;
struct P { int x = 1; };
class Q {
    int x = 2;
public:
    int get() const { return x; }
};
int main() {
    P p;
    Q q;
    p.x = 5;
    cout << p.x << " " << q.get();
}

5. What does the program print?
#include <iostream>
using namespace std;
class Box {
    int size = 1;
public:
    void setSize(int size) {
        if (size > 0) this->size = size;
    }
    int getSize() const { return size; }
};
int main() {
    Box b;
    b.setSize(4);
    b.setSize(-2);
    cout << b.getSize();
}

6. union Value { int i; double d; }; You write v.i = 1; and then v.d = 2.5;. Which member can you read correctly now?

7. What does the program print?
#include <iostream>
using namespace std;
class Counter {
    int value = 0;
public:
    void increment() { value++; }
    int get() const { return value; }
};
void bump(Counter c) { c.increment(); }
int main() {
    Counter a;
    a.increment();
    bump(a);
    Counter b = a;
    b.increment();
    cout << a.get() << " " << b.get();
}

Score: 0/7

Code in repository: L03

Romanian version