// A generic door class (Door.h)#pragma onceclassDoor{protected:bool shut_;public:Door();// Constructs a shut doorboolisOpen()const;// Is the door open?voidopen();// Opens the door, if possiblevoidclose();// Shuts the door};
We used protected so that subclasses can use the attribute as well.
class: Door
objects: BigHouseDoor, BathroomDoor
New Feature - version 2.0
Now the manager has added another feature to implement: track how many door at every instance of the game
An inexperienced would solve the problem by using a global variable and increment each time we added a new door and viceversa.
This complicates the process and can cause errors later down the road.
This also contradicts the OOD principle
Solution
Definition of a static class that is a friend. That counts the amount of doors at run and is incremented by the constuctor of each new door and decrements in the destructor.
Remember: A static method is part of the class and only one instance exists and not a different one for each object
Added features
~Door() will decrement each time a door is removed
the static variable count_
static method getDoorCount, returns count_
Remember: the static variable must be defined init in the .cpp file
Inheritance - version 3.0
There are 2 ways to use a class:
make a new instance of the class (what we did until now)
Inherit from an existing class
Question
Which fields and methods from the base class can be accessed from the derived class?
Answer
public and protected
Therefore, each sub-class of Door can use every method and variable of Door
Example of sub-class
In a game there may be many types of doors. Here we will design a door with a lock
object type:
LockableDoor
attributes:
Open or close
Locked or not locked
methods:
Init as closed door and locked
Open if not locked
Close
State if opened
Lock
Open lock
State if locked
Notice: that this is more compact that Door.h this is because we will reuse Door and dont have to redefine everything a second time.
when we inherit using public before Door we can access any public and protected variables and methods. If not specified the default is private
We could also inherit from multiple classes as so:
Lets now define our .cpp file
and our main.cpp
or in one file for simplicity
What is the size of the following?
This question was asked to me by a fellow student
As we can see even though b doesn't have access to i (unless we use a setter/getter) it will still use memory
Inheritance: order of construction / destruction
Class objects are constructed from the bottom up:
The base.
The members (may want to use init list)
The derived class itself
They are destroyed in the opposite order:
The derived class itself.
The members.
The base.
How to rember: Think about this, first someones parents were born and then him (construction). Now this makes alot of sense because our parent class probably has to initialize a few fields and only after can we use them.
In our LockableDoor example
Doors' constuctor, which inits shut_ and count_
Initialize _locked
LockableDoor constuctor which is empty
Base and Derived Compatibility
Lets say we have 2 functions and 2 objects
Giving a derived type to a base type is compatible
The opposite is not true
and
myDoor = keyedDoor can cause object slicing, since the compiler will ignore any variables or methods not in the base type. We will explore how to solve some of the problems with virtual methods later on.
Has-a and Is-a relationship
Is-A relation- Interface Inheritance: creating a subtype of an existing class for purpose of setting up dynamic binding.
Circle is a subclass of Shape (i.e., Is-A relation).
A Birthday is a subclass of Date.
Has-A relation - Code Reuse: reusing an implementation to create a new class type.
Class Clock that inherits from class Window the ability to represent itself graphically. A stack is not really a subtype or specialization of Vector.
In this case, inheritance makes implementation easier, because there is no rewrite and debug existing code.
This is called using inheritance for reuse, i.e., a pseudo-Has-A relation.
/* Door.cpp */
#include "Door.h"
Door :: Door() : shut_(true) { }
bool Door :: isOpen() const { return !shut_; }
// By default, it's always possible to open
void Door :: open() { shut_ = false; }
void Door :: close() { shut_ = true; }
/* Game.cpp */
#include "Door.h"
int main(){
Door BigHouseDoor;
Door BathroomDoor;
BigHouseDoor.open();
...
...
if ( BigHouseDoor.isOpen() )
enterBigHouse();
else
lookForBackDoor();
}
// A generic door class (Door.h)
#pragma once
class Door {
protected:
bool shut_;
static int count_;
public:
Door(); // Constructs a shut door
bool isOpen() const; // Is the door open?
void open(); // Opens the door, if possible
void close(); // Shuts the door
~Door(); // Destructor
// define a static class method, which return the number
// of Door instances ( objects)
static int getDoorCount();
};
/* Door.cpp */
#include "Door.h"
/* defining the static members first */
int Door::count_ = 0; /* defining and initializing count_*/
int Door::getDoorCount () {
/* this method can't access shut_ !! */
return count_;
}
Door::Door() : shut_(true) {
count_++;
}
bool Door::isOpen() const {
return !shut_;
}
// By default, it's always possible to open
void Door::open() { shut_ = false; }
void Door::close() { shut_ = true; }
Door::~Door() {
count_--;
}
/* Game.cpp */
#include <iostream>
#include "Door.h"
int main() {
Door D;
Door* Dp;
// calling non-static methods of the object
D.open();
D.close();
cout << "At start, we have " << Door::getDoorCount() << " Door(s)" << endl;
// pay attention: when calling static method, you
// should use the scope of the class ( Door::...)
Dp = new Door();
Dp->isOpen();
cout << "After new, we have " << Door::getDoorCount() << " Door(s)" << endl;
delete Dp;
cout << "After delete, we have " << Door::getDoorCount() << " Door(s)" << endl;
}
At start, we have 1 Door(s)
After new, we have 2 Door(s)
After delete, we have 1 Door(s)
// A lockable door class (LockableDoor.h)
#pragma once
#include "Door.h"
class LockableDoor : public Door {
protected:
bool locked_;
public:
LockableDoor(); // Constructs a shut door
bool isLocked() const; // Is the door locked?
void open(); // Opens the door, if not locked
void lock(); // Locks the door
void unlock(); // Unlocks the door
};
class LockableDoor : public Door
class ClockRadio : public Clock, public Radio
// A lockable door class (LockableDoor.cpp)
#pragma once
#include "LockableDoor.h"
LockableDoor::LockableDoor() : Door(), locked_(true) { }
bool LockableDoor :: isLocked() const { return locked_; }
void LockableDoor :: lock() { locked_ = true; }
void LockableDoor :: unlock() { locked_ = false; }
void LockableDoor :: open() {
if (!locked_)
Door::open(); // could use shut_=false too
}
/* Game.cpp */
#include <iostream>
#include "LockableDoor.h"
int main() {
Door generic; // generic.shut_ is true
LockableDoor bathDoor; // bathDoor.shut_ and bathDoor.locked_ are true
generic.open(); // generic.shut_ is false
bathDoor.lock(); // bathDoor.shut_ and bathDoor.locked_ are true
bathDoor.open(); // bathDoor.shut_ and bathDoor.locked_ are still true
if (!bathDoor.isOpen())
bathDoor.unlock(); // bathDoor.shut_ is true and bathDoor.locked_ is false
}
#include <iostream>
using namespace std;
class Door {
protected:
bool shut_;
static int count_;
public:
Door(); // Constructs a shut door
bool isOpen() const; // Is the door open?
void open(); // Opens the door, if possible
void close(); // Shuts the door
~Door(); // Destructor
// define a static class method, which return the number
// of Door instances ( objects)
static int getDoorCount();
};
// A lockable door class (LockableDoor.cpp)
/* defining the static members first */
int Door::count_ = 0; /* defining and initializing count_*/
int Door::getDoorCount () {
/* this method can’t access shut_ !! */
return count_;
}
Door::Door() : shut_(true) {
count_++;
}
bool Door::isOpen() const {
return !shut_;
}
// By default, it's always possible to open
void Door::open() { shut_ = false; }
void Door::close() { shut_ = true; }
Door::~Door() {
count_--;
}
class LockableDoor : public Door {
protected:
bool locked_;
public:
LockableDoor(); // Constructs a shut door
bool isLocked() const; // Is the door locked?
void open(); // Opens the door, if not locked
void lock(); // Locks the door
void unlock(); // Unlocks the door
};
LockableDoor::LockableDoor() : Door(), locked_(true) { }
bool LockableDoor :: isLocked() const { return locked_; }
void LockableDoor :: lock() { locked_ = true; }
void LockableDoor :: unlock() { locked_ = false; }
void LockableDoor :: open() {
if (!locked_)
Door::open(); // could use shut_=false too
}
int main() {
Door generic; // generic.shut_ is true
LockableDoor bathDoor; // bathDoor.shut_ and bathDoor.locked_ are true
generic.open(); // generic.shut_ is false
bathDoor.lock(); // bathDoor.shut_ and bathDoor.locked_ are true
bathDoor.open(); // bathDoor.shut_ and bathDoor.locked_ are still true
if (!bathDoor.isOpen()){
bathDoor.unlock(); // bathDoor.shut_ is true and bathDoor.locked_ is false
cout << "Bathroom door is now unlocked" << endl;
}
}
#include <iostream>
using namespace std;
class A{
private:
int i;
};
class B : public A{
public:
int j;
};
int main(){
A a;
B b;
b.j = 100;
// b.i = 100; won't compile
cout << sizeof(a) << endl;
cout << sizeof(b) << endl;
}
4
8
void f1(LockableDoor d);
void f2(Door d);
Door myDoor;
LockableDoor keyedDoor;
f1(keyedDoor); // is legal
f2(keyedDoor); // is legal
f1(myDoor); // illegal !!!
f2(myDoor); // is legal
myDoor = keyedDoor; // is legal, may slice object
keyedDoor = myDoor; // illegal!!!
// A combination lock door with single Date
class CombinationLockDoor : public LockableDoor {
protected:
const Date combination_;
public:
// Constructs new door with combination c
CombinationLockDoor(const Date& c);
// Attempts to unlock the door with combination c; hides LockableDoor::unlock
void unlock(const Date& c);
};
CombinationLockDoor::CombinationLockDoor(const Date& c)
: lockableDoor(), combination_(c) { }
void CombinationLockDoor :: unlock(const Date& c) {
if (c.theYear() == combination_.theYear() && c.theMonth() == combination_.theMonth() && c.theDay() == combination_.theDay())
LockableDoor :: unlock();
// could use locked_ = false;
}