CSE332S Object-Oriented Programming in C++ (Lecture 16)
Intro to OOP design and principles
Review: Class Design
Designing a class to work well with the STL:
- What operators are required of our class?
operator<for ordered associative containers,operator==for unordered associative containersoperator<<andoperator>>for interacting with iostreams- Algorithms require particular operators as well
Designing a class that manages dynamic resources:
- Must think about copy control
- Shallow copy or deep copy?
- When should the dynamic resources be cleaned up?
- Move semantics for efficiency
OOP Design: How do we combine objects to create complex software?
Goals - Software should be:
- Flexible
- Extensible
- Reusable
Today: 4 Principles of object-oriented programming
- Encapsulation
- Abstraction
- Inheritance
- Polymorphism
Review: Client Code, interface vs. implementation
Today we will focus on a single class or family of classes related via a common base class and client code that interacts with it.
Next time: Combining objects to create more powerful and complex objects
Client code: code that has access to an object (via the object directly, a reference to the object, or a pointer/smart pointer to the object).
- Knows an object’s public interface only, not its implementation.
Interface: The set of all functions/operators (public member variables in C++ as well) a client can request of an object
Implementation: The definition of an object’s interface. State (member variables) and definitions of member functions/operators
Principle 1: Encapsulation
Data and behaviors are encapsulated together behind an interface
- Member functions have direct access to the member variables of the object via “this”
- Benefit: Simplifies function calls (much smaller argument lists)
Proper encapsulation:
- Data of a class remains internal (not enforced in C++)
- Client can only interact with the data of an object via its interface
Benefit:
(Flexible) Reduces impact of change - Easy to change how an object is stored without needing to modify client code that uses the object.
Principle 2: Abstraction
An object presents only the necessary interface to client code
- Hides unnecessary implementation details from the client
a. Member functions that client code does not need should be private or protected
We see abstraction everyday:
- TV
- Cell phone
- Coffee machine
Benefits:
- Reduces code complexity, makes an object easier to use
- (Flexible) Reduces impact of change - internal implementation details can be modified without modification to client code
Principle 3: Inheritance (public inheritance in C++)
“Implementation” inheritance - class inherits interface and implementation of its base class
Benefits:
- Remove redundant code by placing it in a common base class.
- (Reusable) Easily extend a class to add new functionality.
“Interface” inheritance - inherit the interface of the base class only (abstract base class in C++, pure virtual functions)
Benefits:
- Reduce dependencies between base/derived class
- (Flexible, Extensible, Reusable) Program a client to depend on an interface rather than a specific implementation (more on this later)
One More Useful C++ Construct: Multiple Inheritance
C++ allows a class to inherit from more than one base class
class Bear: public ZooAnimal {/*...*/};
class Panda: public Bear, public Endangered {/*...*/};Construction order - all base classes are constructed first:
- all base classes -> derived classes member variables -> constructor body
Destruction order - opposite of construction order:
- Destructor body -> derived classes member variables -> all base class destructors
Rule of thumb: When using multiple inheritance, a class should inherit implementation from a single base class only. Any number of interfaces may be inherited (this is enforced in Java)
Principle 4: Polymorphism
A single interface may have many different implementations (virtual functions and function overriding in C++)
Benefits:
- Avoid nasty switch statements (function calls resolved dynamically)
- (Flexible) Allows the implementation of an interface to change at run-time
Program to an interface
Client should restrict variables to an interface only, not a specific implementation
- Extensible, reusable: New subclasses that define the interface can be created and used without modification to the client. Easy to add new functionality. Easy to reuse client.
- Reduce impact of change: Decouples client from concrete classes it uses.
- Flexible: The implementation of an interface used by the client can change at run-time.
In C++:
- Abstract base class using pure virtual functions to declare the interface
- Implement the interface in subclasses via public inheritance
- Client maintains reference or pointer to the base class
- Calls through the reference or pointer are polymorphic
// declare printable interface
class printable {
public:
virtual void print(ostream &o) = 0;
};
// derived classes defines printable
// interface
class smiley : public printable {
public:
virtual void print(ostream &o) {
o << ":)" ;
};
};
class frown : public printable {
public:
virtual void print(ostream &o) {
o << ":(";
};
};
int main(int argc, char * argv[]) {
smiley s; // s restricted to
// a smiley object
s.print();
// p may point to an object
// of any class that defines
// the printable interface
printable * p =
generateOutput();
// Client unaware of the
// implementation of print()
p->print();
return 0;
}Program to an interface Allows easily extensible designs: anything that defines the printable interface can be used with our client
class Book : public printable {
vector<string> pages;
public:
virtual void print(ostream &o) {
for(unsigned int page = 0; page < pages.size(); ++page){
o << "page: " << page << endl;
o << pages[i] << endl;
};
};