(WIP) Talk: Object-Oriented Programming
Some old notes I took while listening to that talk. Not sure if really of any use though…
Talk: Back to Basics: Object-Oriented Programming - Jon Kalb - CppCon 2019
Resources:
- video on YouTube
- slides
- books:
- Sutter, Alxandrescu - C++ Coding Standards
DISCLAIMER: Modern philosophy is to use value-semantics instead of reference semantics (which OOP makes use of).
Great talk on the basics of “old-school” object-oriented programming (“OOP).
OOP is still in wide spread use, but modern resources (e.g. talks, books) do not focus on it anymore. This talk on the other hand explains how to make use of modern language feature for old-school OOP.
- modern: compile-time
Theory of OOP
- def: polymorphism based on runtime function dispatch using virtual functions
- base class defines API, similar to derived objects are independent libraries
- base class: define API
- derived class: provides different implementations
- pointer to base:
- difference: static vs dynamic type!!!
virtual
method: dispatches to correct type
Liskov substitution
Subtype Requirement: code written against Base API will also work correctly for objects of the derived type
counter example:
struct SurpriseLogger final: Logger {
virtual void LogMessage(char const* message) override {
std::exit(EXIT_FAILURE); // fails to provide semantics of LogMessage API
}
};
OOP: not only hierarchy BUT As we derive we respect requirements of base class
talk: virtual dispatch: inbal levi cppcon 2019
Design guidelines
- Use OOP to model “is-a” relationship, not for code-reuse
- Make non-leaf classes abstract.
- Use the non-virtual interface (NVI) idiom
Building guidelines
Example: Logging
simple example
struct Logger { // base class: defines interface
virtual void LogMessage(char const* message) = 0;
virtual ~Logger() = default;
};
#include <iostream>
struct ConsoleLogger final: Logger { // derived class: provides implementation
virtual void LogMessage(char const* message) override {
std::cout << message << '\n';
}
};
#include <memory>
int main() {
auto logger{std::make_unique<ConsoleLogger>()};
Logger* logger_ptr(logger.get());
logger_ptr->LogMessage("Hello, World!");
// static (compile-time) type of logger_ptr: Logger*
// dynamic (run-time) type of logger_ptr: ConsoleLogger*
}
LogHelloWorld
void LogHelloWorld(Logger& logger) {
// neither knows nor depends on dynamic type of logger
logger.LogMessage("Hello, World!");
}
#include <memory>
int main() {
auto Logger{std::make_unique<ConsoleLogger>()};
LogHelloWorld(*logger);
LogHelloWorld(*static_cast<Logger*>(logger));
}
FileLogger
#include <fstream>
struct FileLogger final : Logger {
FileLogger(char const* filename) : output_{filename} {}
virtual void LogMessage(char const* message) override {
output_ << message << '\n';
}
private:
std::ofstream output_;
}
#include <memory>
int main() {
auto cl{ConsoleLogger{}};
auto fl{FileLogger{"logfile.text"}};
LogHelloWorld(cl);
LogHelloWorld(fl);
}