oop in c++

OOP in C++

OOP is often technically simplified as a principle bundling and encapsulate Data with Logic in Objects. However combining data and behavior can result in tight coupling.

OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. Dr. Alan Kay

The Big Idea is: Messages can passed between objects. [1] An Object can receive Messages and can respond to the sender.

C++ moved to a more generic style of programming. The Standard Libary (STL) uses also a lot of FP Techniques.

The keyword class and signature features like inheritance (subclasses) are linked to the work of Ole-Johan Dahl and Kristen Nygaard (Simula).

  1. Smalltalk
  2. Simula

Access Modifiers

The access specifiers to define the accessibility of members:

  • public
  • private
  • protected

It is a good Idea to prefere to keep members of a class private.

For example day, month, year of a Date Class. This allows Invariations and hides the implementation. It may also allow Caching (lazy-loading) or Iterators.

For example a day setter sets the private day_ of a class Date.

void Date::Day(int day) {
  // Invariation
  if (day >= 1 && day <= DaysOfMonth())
    day_ = day;
}

Instead of date.day = 15, date.Day(15) allows a Validation.

A C++20 std::chrono example that uses another approach and implementation class day.

    std::chrono::day d1{15};
    if (d1.ok()) {
        std::cout << " is a valid day.\n";
    } 

The Date Class could also use std::chrono, because private members with accessors are hiding the implementation and the public interface stays stable and constant.

Protected data is a bad idea.

Accessor and Mutator methods

Accessors (or Getter Methods) and Mutators (or Setter Methods) are to control the private members mutation. This allows invariant logic (for example: conditional set valid values only).

Encapsulation

Keep the internal State of an object private and only through an Interface. Allows Iterators or Invariations over Setters.

Encapsulation may hide underlying data strucures which may contain logic. Direct usage of Objects that are not relevant in the Context causes unnecessary tight coupling.

person.head.Mouth().Talk("Hello")

If the person forget its head, things would just work, because “Say” handles how to say “Hello”:

person.Say("Hello");

A famous Style Guideline is:

“Only talk to your immediate friends.” Law of Demeter

SelfEncapsulation

The Usage of Encapsulation for mutable internal Data. Only the public interface or introduced private acccessors are allowed to mutate internal data.

Mutation of accessible members:

bool Date::IsLeapYear() {
  if (year_ % 4 == 0) {
    return false;
  }
  else if (year_ % 100 == 0) {
    return true;
  }
  else if (year_ % 400 != 0) {
    return false;
  }
  return true;
} 

Access through Accessors (SelfEncapsulation):

bool Date::IsLeapYear() {
  if (Year() % 4 == 0) {
    return false;
  }
  else if (Year() % 100 == 0) {
    return true;
  }
  else if (Year() % 400 != 0) {
    return false;
  }
  return true;
} 

SelfEncapsulation

Initializer List

Initilize Values in the Constructor.

Date(int day, int month, int year) : day_(day), month_(month), year_(year) {}

Initializer list doesn’t allow invariants.

Date(int day, int month, int year) : day_(day), month_(month), year_(year) { 
  ValidateMembers(); 
}

Initializer lists are a way to initiliaze const members (for example a imutable class Birthday).

Static variables and methods

Static variables are Class-Variables, that are not tied to an instance of an object and like namespaced global Variables.

A static const members has to be initilized out of the class or as constexp.

Polymorphism

Polymorphism allows different Implementations (Variants) with a similiar interface.

Polymorphism is often based on virtual (overloaded) functions. Like a virtual table of objects and you set the pointer to the implementation with an overloaded function that the dispatcher calls.

A new Kind of Polymorphism, possible in C++17, is over std::visit to visit a variant of std::variant at runtime and the best matching implementation (variant) get called.

Inheritance

Reuse and inherit features (attributes) from a exisiting class.

A Car can be FireDepartmentCar with special Abilities, but both are also an Vehicle. In Object Hierachy, Vehicle <- is_a Car <- is_a FireDepartmentCar over Inheritance. A Subtype is therefore also a “specialized” BaseType.

Building complex hierachies may fail, because a subtype may be harder to replace (Liskov Substitution Principle). For example when the FireDepartmentCar extends EmergencyGadgets, which also could get extended by an HospitalHelicopter (which is not a Vehicle) … do the same Emergency-Policies (can ignore traffic lights etc.) apply? Is it just a flyable Vehicle?
In that Case it may better to implement an abstract EmergencyGadget[1].

  1. Program to an interface, not an implementation by Erich Gamma

Access specifier of derived Class

Multiple Inheritance

To inherit features from more than one base class is possible.

Use Case: a Class that inherits different implementations that is not based on a single-rooted hierachy to represent multiple distinct interfaces.

Diamond Conflict.

AmphibiousCar <- is_a Boat, is_a Car <- is_a Vehicle

For example an amphibious Car, that inherits Boat (Boat#move) and Car (Car#move), both implement move, inherited from Vehicle (virtual Vehicle#move): Which parent implemenation of Vehicle#move get executed?

Generic Programming

A generic algorithm for differnt data-types with templates. Templates can be combined with functional programming patterns and techniques. For example the STL uses generic and functional programming Techniques. Callable objects are used in many STL algorithms and generic template vector.

Functional Programming

Often in a more verbose syntax, than in a GarbageCollected-Language, but without the Garbage (Resource acquisition is initialization (RAII)).

Beyond C function pointers:

  • C++03: named Functors ()
  • C++11: lambdas (and IIFE - Immediately Invoked Function Expression)
  • C++14: polymorphic anonymous functions

historical WhatIs? Paper

C++, formerly “C with Classes”, is object-oriented programming on top of C and follows more a Simula Model and not a dynamically typed language such as Smalltalk.

Object-oriented Programming is programming using inheritance. Data abstraction is programming using user defined type. With few exceptions, object-oriented Programming can and ought to be a superset of data abstraction. These techniques need proper support to be effective. Data abstraction primarily needs support in the form of language features, and object-oriented programming needs further support from a programming environment. To be general purpose, a language supporting data abstraction or object-oriented programming must enable effective use of traditonal hardware.

([1] What is “object-oriented programming”? Stroustrup 1986)

There is also an powerful example of virtual functions (“parametrized types”) under 4.2 Type Checking.

  1. What is “object-oriented programming”? Paper, Stroustrup 1986
Written on September 11, 2020
[ c++  oop  ]