What is object oriented programming
What is object oriented programming
What is Object Oriented Programming? (Understanding Encapsulation, Abstraction, Inheritance, Polymorphism)
I’ve been asked during interviews if I know what Object Oriented Programming is? Or why would I use OOP over Functional programming? For the most part I have used OOP and have built projects, but now I have write this article. Hopefully this helps any new developers have a clear explanation and help them along in their coding journey.
If you are to take anything away from this article is that what makes OOP awesome is the four pillars, which are Encapsulation, Abstraction, Inheritance, Polymorphism.
TLDR: The four pillars of OOP are Encapsulation, Abstraction, Inheritance, Polymorphism. Each provides a way to keep your data save and create objects. Classes can pass down data or methods through Inheritance. Abstraction is to have something difficult and turn it into smaller parts that eventually make up that complete object.
What is Object Oriented Programming (OOP)?
So, what does this all mean? I’ll try to keep it simple by explaining any keywords necessary to understand class based OOP followed by breakdown.
OOP uses classes to setup your program. Within these classes you have to initialize any new objects your are trying to create through an initialize method or constructor (JS). In the example below we create the a new object instance of the Bike class.
Lets say we were building a vehicle racing game that can have up to four players (We will build a vehicle class later); In this case we are building a Bike class. Every player will use this class to create a new instance of the object class Bike. Before the start of the game every place needs to select their bike and attributes (i.e type vehicle such as car, bike, motorcycle, electric). The Bike class helps with this by allowing you to select the type, size, and amount of wheels. By default we are setting the wheels to two since bicycle by definition have 2 wheels.
The example above is a simple way of how class based OOP is created.
Next, I will cover the main principles that make up OOP.
The 4 Principles of Object Oriented Programming
Encapsulation
Encapsulation is the process of combining data and functions into a single unit called a class; it keeps data both safe from outside interference and misuse. With Encapsulation, the data is not accessed directly. It can only be accessed through public functions present inside the class called methods. Encapsulation is achieved by keeping it’s state private.
Short: Encapsulation allows for us to create methods that can only be accessed or changed by public methods.
Below I have created the Vehicle Class. If we are driving a vehicle and the character gets hit than that will determine how much health and damage they take. We may want to keep the data within these methods private because within this racing game another class may be SUV or Car which would have different rates of receiving damage or being able to regain health if our racing game allowed for vehicle to be destroyed (i.e. Mario Kart or Twisted Metal).
Abstraction
The main purpose of Abstraction is to handle the complexity of the program by hiding unnecessary detail from the user. This allows users to implement advance logic without having to understand or to think about all the hidden complexity.
Continuing with our Racing game which allows users to drive and inflict damage on other users vehicles. Let’s look at the remote controller.
If we go back in time to Super Nintendo or the PS1 we have a simple controller with limited buttons that each perform a task. Each of the buttons causes something to happen when driving the car, whether it’s going up or down, accelerating or breaking, or activating your weapon. For the user all they know is that when they press a certain button it causes X action. So even when we come back to the future and talk about Mario Kart 10 the user knows the basics and may just need to learn some added twists. Overall, we have abstracted concepts in the back by creating classes that have a specific task and try to mirror real world actions.
As an example, we could abstract the following from the racing game and when we make improvements the user will not know the difference since for the most part we have abstracted the program for the user.
Inheritance
Inheritance is the concept of having a class inherit from another class because it allows for code to be reused.
An example of this would be if we had a vehicle class and then we created classes that inherited from the parent Vehicle class.
In the example above because we added the “ Motorcycle class which allows us to call the accelerate method or any other method that is present within the vehicle class. However, if we were to declare a private method within the vehicle class, that method would not be available to any inherited class.
There is a way to override using public methods used in the parent class and that will be explained with Polymorphism.
Polymorphism
Polymorphism comes from the latin word meaning many forms. We can achieve Polymorphism through Inheritance as mentioned above on how classes can inherit methods from parent classes.
You can override any inherited method by creating your own method within it’s own class with the same name or variable.; checkout the ElectricSedan Class example under Inheritance.
In the example above, we overrode the ACCELERATE_SPEED from 2 to 4. The main reason is that a motorcycle usually accelerates faster than a larger vehicle. For the most part, this covers some of the common uses of Polymorphism. This can be achieved with most, if not all programming languages out there (don’t know all of them so I am not sure).
Chapter 3: What is Object-Oriented Programming?
O bject-oriented programming (or OOP) is a paradigm or pattern of programming whereby the solution to a programming problem is modelled as a collection of collaborating objects. Objects collaborate by sending messages to each other. It is most suitable for managing large, complex problems.
An object is an entity that possesses both state (or properties or attributes) and behaviour. Put another way, an object encapsulates data and the functions that operate on that data. The data is usually hidden from other objects so that the only way to affect the data is through the object’s functions (or methods).
An example of an object is a car. A car has attributes (e.g., colour, size, weight, fuel capacity, number of passengers, etc.). A car has behaviour represented by its methods (e.g., start engine, turn left/right, accelerate, stop, turn on wipers, etc.).
A class is a special kind of object that’s used as a template for creating instances of itself. Think of it like a cookie cutter that produces cookies (or objects).
A class can inherit the attributes and behaviour of another class (its parent), and it can modify or customize that behaviour (i.e., its methods) for itself. This leads to the concept of polymorphism. Polymorphism means that when an object receives a message, the correct method is called, based on the object’s class. That method may belong to the parent, or it may be one that is customized for this class.
Here’s an example of polymorphism:
Smalltalk allows a class to inherit from only one class. Some OOP languages allow a class to inherit from several classes; this is known as multiple inheritance. Multiple inheritance causes a great deal of complexity, which is why it is generally avoided. We shall not speak of multiple inheritance again.
While inheritance is an important aspect of OOP, it is not the only way to build up programs. Instead of inheritance, composition or aggregation can be used. A class may include instances of other classes without inheriting anything. It’s a “has a” relationship as in: Class A has a Class B instance as a member. If you used inheritance, then Class B would be a kind of Class A object; this is an “is a kind of” relationship. Let’s illustrate this with examples…
A car is a kind of motorized vehicle (the parent class). So is a motorcycle. So is a motor boat. So is an aircraft. Each of these can inherit the attributes and behaviour of a motorized vehicle. But they can also customize the attributes and methods of the parent class for themselves.
A car has other objects or classes as part of itself, such as an engine, wheels, steering wheel, etc. It does not inherit anything from these classes.
Syntactically, the attributes of an object (the object’s data) are represented by instance variables. Typically, you will create “getter” methods (get the value of an instance variable) and “setter” methods (set or modify the value of an instance variable) for them, since instance variables are hidden from the outside world.
In Pharo, instance variables are created in the class definition, for example:
In the #Time class (the hash in #Time designates Time as a Smalltalk symbol), there are two instance variables: ‘seconds’ and ‘nanos’. The object’s methods will operate on these variables, which represent the hidden and internal state of the object.
By the way, the #Time class derives from, or inherits from, the #Magnitude class. Alternatively, you can say the Magnitude class “subclasses” the Time class. This is typical Smalltalk parlance.
An object resembles the conventional module concept that is used in simpler procedural programming languages like C and Pascal. A module contains a data structure and the functions that operate on the structure. The data, however, is not hidden; anybody can access the data. Most importantly, modules cannot inherit from other modules.
Objects are usually much finer-grained than modules. Thus, they are ideal for modelling complex systems.
So that’s it. That’s your introduction to Object-Oriented Programming. If you want to study this interesting subject in greater depth, there is a wealth of books and material dedicated to OOP and Smalltalk, for example:
What Is Object Oriented Programming?
Classical Definition of OOP
Object Oriented Programming (OOP) is a programming paradigm which tries to emulate real world problems using the abstract concept of ‘objects’ as a data structure which can be mutated and referenced.
There is no dictionary definition of the phrase OOP, but we can refer to Wikipedia’s summary to glean a semi-‘official’ account of its use:
My Background in OOP
I have personally been programming for around 4 years as a hobbyist, student and professional developer. Academically, I hold a short-course certification in Object Oriented Programming with Java from City University, London amongst other various courses I have studied and books I have read.
The above does not make me an authority on this subject; but I feel I can certainly help those searching for an initial understanding and conceptual brief of OOP.
It’s imagined that if you are reading this article and have found it through search, you don’t yet have a mental concept of OOP nor have implemented it in practice. Let me assist you there.
How can learning OOP benefit me?
It is certainly a concept that is extremely useful for budding developers and programmers to understand, as approaching software or web development projects using OOP (as opposed to procedural approaches), cuts development time, reduces repetition and leads to an easier to produce, maintain and understand, codebase.
Not only that, but pretty much every professional developer uses objects and classes as part of their data structures.
If you don’t understand objects and the basic principles of OOP, you won’t survive very long as a professional developer, or at least you will be limited in your scope for producing more complex applications than perhaps a static simple program that computes values.
To get you to a better understanding of OOP, I will use code examples using the highly popular JavaScript ES6 language as reference to highlight concepts discussed.
I have deliberately omitted from this article, all aspects of OOP, as I feel it is important only to understand a few major concepts, before diving deeper. In this way, I’ve abstracted OOP for the beginner in a way that can be understood with perhaps a little programming background.
Approaching Programming Problems: Procedural versus OOP
To better understand OOP, you must understand programming in its infancy, before the creators of our modern languages modified popular high level programming languages to use objects to overcome the difficulties of time consuming, procedural — line by line — programming approaches.
Infancy of Computing — Pre OOP
Very simple definitions of programming often refer to giving the computer an algorithm, which we understand as a simple set of logical instructions that lead to a set outcome. A common metaphor used might be of baking a cake.
We can understand in the English language that to “bake a cake” is an abstraction of a more refined process, which we can further break down into simple instructions. E.G 1. Add flour. 2. Mix and Beat Eggs…etc etc
Computers, at their heart, are ‘simple’ machines in this respect, that can compute or process instructions at lightening speed, depending on various factors. We can give the computer these instructions procedurally — one after another, with intention that they are executed consecutively and iteratively.
Instructions at their rawest form are binary — they are read at a low level as a series of 1s and 0s e.g 10010100, interpreted via electrical signals that are on or off, high or low or even for example in the change in dimples on the surface of a CD or DVD.
Humans can’t instruct computers to do what they want (or program them) in this form due to the sheer volume of bits required to communicate simple instructions.
It just isn’t practical to write binary or machine level code these days, although it was certainly attempted by historical computer scientists — such as Ada Lovelace and Charles Babbage. They produced programs that solved basic mathematical computations; but the machines they used and the tools available at the time (often using punch cards) were very limited in their processing power and ability to execute these binary instructions.
As a result, those that developed modern computing, abstracted the low level instructions to a higher level language — one that humans can interpret and understand quickly, with a little knowledge and training.
The following is a simple snippet of a procedural code base that produces the square of a number.
This is fairly useful for its given case, which was to find the square of 2 and store it within a mutable variable called ‘squareOf2’.
However, writing code like this is fairly useless if we want to compute given cases in the future without re-writing a statement for each case.
For example, to square all the numbers from 1 to 10, you’d have to write something like this…
The previous example highlighted the caveat of a procedural approach to programming. To achieve outcomes, solve problems or implement algorithms, we’d have to write the same thing over and over again top to bottom.
We’d repeat ourselves throughout the program and our final product will be one very long code file which would be hard to maintain.
To overcome these obstacles, developers of modern ‘high level’ programming languages (understood as — abstracting or summarising lower level instructions to more succinct operations) produced a functional programming approach, which allowed the developer the flexibility of encapsulating or ‘capturing’ their what would otherwise be repetitive coding behaviours, into easy to reference functions, that could be named and reused.
That is, rather than re-inventing the wheel every-time, as in the prior example, programmers can now simply call a function they write once and change the input argument (data) as they see fit.
Take the following example of a ES6 arrow-notation function below, that encapsulates the above process and solves the algorithmic challenge of squaring a given number (an argument) multiple times:
If you don’t know JavaScript all that well; to highlight the above notation bit by bit, I have simply assigned to a constant (in most cases, immutable variable) called square, a function which takes ‘base’ (a parameter) which can be anything the user chooses to pass in; and then run a simple calculation on it to square whatever number the user passes in.
Therefore, the outcome of this functional process to square all the numbers from 1 to 10 could be achieved by re-using the above code in the following form (yes, it’s also not ideal — we’ll get to that!).
We can further make this process even simpler for a future use case.
What if we had to square not 10, but 100 or 1000 or more numbers in a go?
Even with the function above, this would still take a little bit of repetitive writing to achieve.
To solve this problem, we could implement a flexible algorithm into a function which allows the user to pass in a single integer as an argument that determines the number of square numbers to compute and when called, executes that calculation as many times as specified.
As below, here is my example of a function that would achieve such an outcome using a conditional operator (a while loop):
See the difference in a functional approach?
We are moving away from the procedural — statement by statement programming, to a more modular, repeatable and encapsulated form.
The end result is that we can write functions that solve specific, broken down algorithms, or parts of yet an even larger algorithm, and use these throughout the program with much more ease than repeating or re-inventing the wheel on each occasion. This also reduces the scope for human typing errors.
That’s great, but what about Object Oriented Programming — you haven’t got to the point yet? What is an Object?
So we understand now, having read the previous summaries, that a functional approach to programming leads to less repetition and abstraction of real world problems into a form of language that we can reuse and refer to throughout the program.
I turned the repetitive process of ‘squaring’ any number into something I could reference in the future simply using the word square().
I no longer had to re-write everything that was encapsulated within that square function, every time I wanted to square a set of numbers.
Objects are yet another form of data structure within many programming languages, often referred to as Object Oriented languages. I use JavaScript throughout this article — but perhaps the best known example of an OOP is the Java language (which has some similarities to JavaScript, but is certainly not a parent or predecessor / relative of it).
If you’ve been programming a little in the past but don’t yet understand Objects or OOP, you might understand simple data structures at this stage.
Objects are a data structure that model real world ‘objects’. They behave like a little more advanced version of an array — but are extremely flexible and easily manipulated.
You may know and have very likely encountered variables (one simple and fundamental way to play with data).
Variables reference a location in memory within the computer and can either be immutable or mutable — that is they can be static / unchangeable or changeable, which we reference in the JavaScript ES6 notation as either let, var or const.
The following code uses mutable variables (using the let keyword to denote a variable is being declared) to hold data:
This is great — we can assign bits of primitive data, such as strings of text, numbers or boolean values to a space in memory which we can later reference or manipulate.
You may be aware of arrays, for example, checkout the following ‘array’ (or in English — consecutive grouping of things stored in memory) and understand that it is just yet another way we structure data:
This is useful, as we can store information in this human understandable form, iterate over it in various ways or manipulate it.
It represents a kind of logical list of things we want to store, rather than a variable, which is just a singular value (not a list of values).
I understand variables, arrays and so on, but what is an object?
Objects take the idea of data structures one step further with the goal of giving the programmer flexibility in modelling within them, real world objects I.E things we encounter within the world outside of computers, such cars, tables, music albums etc.
Pretty much anything you can dream up of, could be in someway represented computationally as an object.
As long as the real world ‘object’ we are trying to capture or abstract into the computer has some form of logic to it (i.e. we cannot model a jumbled set of nothing), we can turn it into a very flexible data structure, called an object.
The following is an example of how to declare a mutable object ‘literal’ in JavaScript ES6:
It is that simple to declare an object, but what sets objects apart from other primitive data structures?
Objects have the following attributes to them in their most basic form:
Let’s take a look at an object in practice using JavaScript again:
The above is great for a single use case of a ‘Volvo 2′ car.
As above, we defined the volvo object, gave it some properties (model, age) and behaviour (a method called findSaleDate() that when called returns the age of the volvo plus 10.
This would be helpful in a program specific to volvo cars only.
However, what if we wanted to go a step further and really implement objects for a large code base with the aim of reducing repetition and abstracting these real world objects to reusable, modular entities?
As above, you have seen that objects are just another data structure, which we can manipulate and are a little more flexible than variables and arrays, for example, as the programmer can define the properties of the object as they wish and subsequently, how it behaves.
You saw that we defined an object literal in a similar way to how we define a variable, but using <> after the equality operator.
We then enclosed within that block, properties and behaviour in the form of key value pairs for the properties, and what are very similar to functions, as the methods, getters, setters, which make up the overall ‘behaviour’ or ‘doing’ capabilities of the object.
I have omitted from this article, the subtleties of privacy that many programmers implement in objects, for example, by denoting properties the programmer does NOT want to be changed directly as beginning the key with an underscore. E.G _name : volvo.
To understand how to build objects properly within JavaScript or your given object oriented language, you really need to understand data structures and then work on the syntax for objects, which is out of the scope of this article, that has been written with aim at developing a beginner’s understanding of OOP on a high level.
Going One Step Further — Objects and Classes
In this final and advanced stage of understanding OOP on a high level, you have so far encountered objects as a data structure which allows the programmer a great deal of flexibility in defining the objects makeup and behaviour.
Classes act as a blueprint for building objects. We could think carefully about the prior scenario, and create a blueprint for building cars (a Car class) which itself produces car objects, which can be defined by the developer or user.
There could also be sub-classes to the car class, for example, a convertible class, or a sports class or whatever is required for the use case of the program.
This is where inheritance comes into play.
There are several main concepts to OOP which I will summarise at the end of this article, but of the most important (bar encapsulation — which you will have understood in the functional programming explanations) is perhaps inheritance.
Inheritance — Parent and Child Classes
As in nature, or simple biology, children inherit features of their parents, within the animal and plant kingdoms. This rings true with the programming paradigm of OOP too.
Let us structure a ‘car factory’ program which has a very high level purpose of producing different cars of different types including sports cars, convertibles and 4×4 jeeps.
We could create a super (parent) class which holds all of the common features (attributes or behaviour) of the objects it is supposed to be a blueprint for.
We could then write further sub or child classes, which inherit from this parent class, those common features, but have specific or custom features that relate only to that child class also. In plain English, we could refer to this child classes as ‘custom classes’ (although this terminology is not used professionally) — as they customise further the blueprint denoted by the parent.
Child classes inherit all of the properties and behaviour of their parent class — in programming lingo, they ‘extend’ the superclass.
These properties and behaviours do not need to be re-written in majority case.
Take the following example of a parent class to catch all cars features where possible (ignore the constructor for now):
The above defines a ‘Car’ class, which has a constructor (a method that initialises or ‘constructs’ the object).
The constructor defines that, that particular object which is being constructed will have a model property that holds the specific value passed in the parenthesis ( model, colour). The same action is taken for colour.
Then under behaviour, there is a printCarSummary() method, which simply returns a string summarising the values of the object (model, colour and age).
We can produce a ‘new’ object instance of Car using the Car class as a blueprint utilising the following syntax.
We can then access that object’s specifically defined state and behaviours however we wish.
The prior parent ‘Car’ object isn’t useful for specific use cases — what if we wanted to quickly define convertibles and jeeps?
Let’s just use a convertible as an example and define a convertible class that inherits all of the car classes properties and behaviour, but has own properties that are unique to that convertible class, such as ‘isHardTop’ — a boolean value denoting whether the convertible is a hard top or not.
Now we have a child Convertible class, which inherits from its parent Car class, all of those features, but adds or extends its own features within the new class.
For example, we don’t have to re-write the printCarSummary() method because it is inherited from the parent Car class AND we can now access the fastCarNoise(), which is specific to the Convertible class.
You can see now that it is much easier to encapsulate general or common attributes of objects in parent classes, and then where there is a specific use case, create child classes that can be used to create even more granular objects.
Summary — What is Object Oriented Programming?
OOP is a way of thinking and a form of tools within programming that implements syntax enabling us to go beyond the old style procedural programming paradigm of trying to solve problems iteratively top to bottom in a slow and logical fashion that ultimately leads to repetition and time consumption.
We can think of objects now in a multidimensional fashion, as another data structure, which is highly flexible, reusable and great for abstracting a problem in a way humans can more easily understand.
Objects are summarised as having properties and behaviour.
Seeing a program, for example, a car factory system, as one which denotes classes (blueprints for objects) in a parent > child fashion — allows us to be very modular in how we produce objects for later manipulation or access.
In totality, objects allow us to understand programming in a more real world fashion, beyond top to bottom instructions for a simple machine, pushing conceptually closer to human ways of processing information i.e. understanding the world in programs as physical objects that have a state (properties and values) and do things or act. OOP in short, makes life a lot easier.
Further Reading — OOP All Concepts
I have only included in this article the main concepts I think you need to understand for you to achieve at least a first grip of OOP on a high level.
I strongly recommend, however, that you explore and read about all of the concepts within OOP to get a deeper understanding.
I discussed abstraction, in the form of looking at how we take a complex task and ‘abstract’ or reduce it to a smaller or more modular task when writing functions.
Encapsulation could also be understood throughout this article in some form through looking at how certain features of objects (or even functions) could be ‘captured’ or ‘encapsulated’ within their own environments.
The major topic of inheritance was discussed using examples of parent (super) classes and sub or child classes.
The following graphical representation highlights the major components of OOP in the Java programming language, which may be relevant depending on your use case:
Key Concepts Discussed in this Article
1. Procedural versus Object Oriented Programming — OOP enables greater flexibility and is closer to producing real world implementations of real world problems, than the simple algorithm implementations we first looked at in the form of baking a cake, following instructions top to bottom.
2. Functional Programming — this is a modern approach to writing programs, in the form of using functions (modular bits of code that we can re-use). OOP takes this concept and advances it.
3. Objects are another data structure — at their rawest form, objects are an advanced data structure which allows the programmer the ability to define properties and behaviour of those objects.
4. Classes — are blueprints for making objects, they are used to specify the makeup of objects. Super (or parent) classes pass all features down to further sub-classes or child classes, which themselves can have specific features. We use the classes to make new objects, typically using the ‘new’ keyword.
I hope you found this article useful. Please feel free to reach out to me directly if you notice any major discrepancies or you feel there is an error somewhere.
To find out more about me, feel free to follow me on Twitter:
Key Concept:
One of the most effective ways to really understand OOP is to see how the main OOP concepts are ultimately implemented at the low-level. This article uses this approach by describing how to implement OOP in the C programming language. Experience shows that software developers, who understand the concepts at the low-level apply them more efficiently and with greater confidence than developers, who only encountered the concepts at the higher-level.
Code accompanying this article is available on GitHub
What is Object-Oriented Programming?
Object-oriented programming (OOP) is a way of design based on the three fundamental concepts:
Object-Oriented Programming in C
Although the fundamental OOP concepts have been traditionally associated with object-oriented languages, such as Smalltalk, C++, or Java, you can implement them in almost any programming language including portable, standard-compliant C (ISO-C90 Standard).
NOTES:
If you simply develop end-user programs in C, but you also want to do OOP, you probably should be using C++ instead of C. Compared to C++, OOP in C can be cumbersome and error-prone, and rarely offers any performance advantage.
However, if you build software libraries or frameworks the OOP concepts can be very useful as the primary mechanisms of organizing the code. In that case, most difficulties of doing OOP in C can be confined to the library and can be effectively hidden from the application developers. This document has this primary use case in mind.
This article describes how OOP is implemented in the QP/C real-time framework. As a user of these frameworks, you need to understand the techniques, because you will need to apply them also to your own application-level code. But these techniques are not limited only to developing QP/C applications and are applicable generally to any C program.
Encapsulation (Classes)
You can very easily apply these design principles to come up with your own “classes”. For example, suppose you have an application that employs two-dimensional geometric shapes (perhaps to be rendered on an embedded graphic LCD).
The basic Shape “class” in C can be declared as follows (NOTE: The code corresponding to this section is located in the sub-directory: oop_in_c/encapsulation/ ):
One nice aspect of classes is that they can be drawn in diagrams, which show the class name, attributes, operations, and relationships among classes. The following figure shows the UML class diagram of the Shape class:
NOTES:
The «me» pointer in C corresponds directly to the implicit «this» pointer in C++. The «this» identifier is not used, however, because it is a keyword in C++ and such a program wouldn’t compile with a C++ compiler.
Inheritance
Inheritance is the ability to define new classes based on existing classes in order to reuse and organize code. You can easily implement single inheritance in C by literally embedding the inherited class attribute structure as the first member of the derived class attribute structure.
For example, instead of creating a Rectangle class from scratch, you can inherit most what’s common from the already existing Shape class and add only what’s different for rectangles. Here’s how you declare the Rectangle “class” (NOTE: The code corresponding to this section is located in the sub-directory: oop_in_c/inheritance/ )
With this arrangement, you can always safely pass a pointer to Rectangle to any C function that expects a pointer to Shape. Specifically, all functions from the Shape class (called the superclass or the base class) are automatically available to the code>Rectangle/code>class (called the subclass or the derived class). So, not only all attributes, but also all functions from the superclass are inherited by all subclasses.
NOTES:
The alignment of the Rectangle structure and the inherited attributes from the Shape structure is guaranteed by the C Standard WG14/N1124. Section 6.7.2.1.13 of this Standard, says: “… A pointer to a structure object, suitably converted, points to its initial member. There may be unnamed padding within a structure object, but not at its beginning”.
As you can see, to call the inherited functions you need to either explicitly up-cast the first “me” parameter to the superclass (Shape *), or alternatively, you can avoid casting and take the address of the member “super” ( &r2->super ).
NOTES:
There are no additional costs to using the «inherited» functions for instances of the subclasses. In other words, the cost of calling a function for an object of a subclass is exactly as expensive as calling the same function for an object of the superclass. This overhead is also very similar (identical really) as in C++.
Polymorphism
Polymorphism is the ability to substitute objects of matching interfaces for one another at run-time. C++ implements polymorphism with virtual functions. In C, you can also implement virtual functions in a number of ways [1,4,10]. The implementation presented here (and used in the QP/C real-time framework) has very similar performance and memory overhead as virtual functions in C++ [4,7,8].
As an example of how virtual functions could be useful, consider again the Shape class introduced before. This class could provide many more useful operations, such as area() (to let the shape compute its own area) or draw() (to let the shape draw itself on the screen), etc. But the trouble is that the Shape class cannot provide the actual implementation of such operations, because Shape is too abstract and doesn’t “know” how to calculate, say its own area. The computation will be very different for a Rectangle subclass (width * height) than for the Circle subclass (pi * radius2). However, this does not mean that Shape cannot provide at least the interface for the operations, like Shape_area() or Shape_draw(), as follows (NOTE: The code corresponding to this section is located in the sub-directory: oop_in_c/polymorphism/) :
In fact, such an interface could be very useful, because it would allow you to write generic code to manipulate shapes uniformly. For example, given such an interface, you will be able to write a generic function to draw all shapes on the screen or to find the largest shape (with the largest area). This might sound a bit theoretical at this point, but it will become more clear when you see the actual code later in this Section.
Virtual Table (vtbl) and Virtual Pointer (vptr)
By now it should be clear that a single virtual function, such as Shape_area(), can have many different implementations in the subclasses of Shape. For example, the Rectangle subclass of Shape will have a different way of calculating its area than the Circle subclass.
This means that a virtual function call cannot be resolved at link-time, as it is done for ordinary function calls in C, because the actual version of the function to call depends on the type of the object (Rectangle, Circle, etc.) So, instead the binding between the invocation of a virtual function and the actual implementation must happen at run-time, which is called late binding (as opposed to the link-time binding, which is also called early binding).
Practically all C++ compilers implement late binding by means of one Virtual Table (vtbl) per class and a Virtual Pointer (vptr) per each object [4,7]. This method can be applied to C as well.
Virtual Table is a table of function pointers corresponding to the virtual functions introduced by the class. In C, a Virtual Table can be emulated by a structure of pointers-to-functions, as shown in Listing 7(2-4).
Virtual Pointer (vptr) is a pointer to the Virtual Table of the class. This pointer must be present in every instance (object) of the class, and so it must go into the attribute structure of the class. For example, the attribute structure of the Shape class augmented with the vptr member added at the top, as sown in Listing 7(1).
The vptr is declared as pointer to an immutable object (see the const keyword in front of the *), because the Virtual Table should not be changed and is, in fact, allocated in ROM.
The Virtual Pointer (vptr) is inherited by all subclasses, so the vptr of the Shape class will be automatically available in all its subclasses, such as Rectangle, Circle, etc.
Setting the vptr in the Constructor
The Virtual Pointer (vptr) must be initialized to point to the corresponding Virtual Table (vtbl) in every instance (object) of a class. The ideal place to perform such initialization is the class’ constructor. In fact, this is exactly where the C++ compilers generate an implicit initialization of the vptr.
In C, you need to initialize the vptr explicitly. Here is an example of setting up the vtbl and the initialization of the vptr in the Shape’s constructor:
Object-oriented programming
Object-oriented programming (OOP) is a programming paradigm fundamental to many programming languages, including Java and C++. In this article, we’ll provide an overview of the basic concepts of OOP. We’ll describe three main concepts: classes and instances, inheritance, and encapsulation. For now, we’ll describe these concepts without reference to JavaScript in particular, so all the examples are given in pseudocode.
Note: To be precise, the features described here are of a particular style of OOP called class-based or «classical» OOP. When people talk about OOP, this is generally the type that they mean.
After that, in JavaScript, we’ll look at how constructors and the prototype chain relate to these OOP concepts, and how they differ. In the next article, we’ll look at some additional features of JavaScript that make it easier to implement object-oriented programs.
Prerequisites: | Understanding JavaScript functions, familiarity with JavaScript basics (see First steps and Building blocks), and OOJS basics (see Introduction to objects and Object prototypes). |
---|---|
Objective: | To understand the basic concepts of class-based object-oriented programming. |
Object-oriented programming is about modeling a system as a collection of objects, where each object represents some particular aspect of the system. Objects contain both functions (or methods) and data. An object provides a public interface to other code that wants to use it but maintains its own private, internal state; other parts of the system don’t have to care about what is going on inside the object.
Classes and instances
When we model a problem in terms of objects in OOP, we create abstract definitions representing the types of objects we want to have in our system. For example, if we were modeling a school, we might want to have objects representing professors. Every professor has some properties in common: they all have a name and a subject that they teach. Additionally, every professor can do certain things: they can all grade a paper and they can introduce themselves to their students at the start of the year, for example.
So Professor could be a class in our system. The definition of the class lists the data and methods that every professor has.
In pseudocode, a Professor class could be written like this:
This defines a Professor class with:
On its own, a class doesn’t do anything: it’s a kind of template for creating concrete objects of that type. Each concrete professor we create is called an instance of the Professor class. The process of creating an instance is performed by a special function called a constructor. We pass values to the constructor for any internal state that we want to initialize in the new instance.
Generally, the constructor is written out as part of the class definition, and it usually has the same name as the class itself:
This constructor takes two parameters, so we can initialize the name and teaches properties when we create a new concrete professor.
Now that we have a constructor, we can create some professors. Programming languages often use the keyword new to signal that a constructor is being called.
This creates two objects, both instances of the Professor class.
Inheritance
Suppose in our school we also want to represent students. Unlike professors, students can’t grade papers, don’t teach a particular subject, and belong to a particular year.
However, students do have a name and may also want to introduce themselves, so we might write out the definition of a student class like this:
It would be helpful if we could represent the fact that students and professors share some properties, or more accurately, the fact that on some level, they are the same kind of thing. Inheritance lets us do this.
You might notice that introduceSelf() is defined in all three classes. The reason for this is that while all people want to introduce themselves, the way they do so is different:
We might have a default implementation of introduceSelf() for people who aren’t students or professors:
Encapsulation
Objects provide an interface to other code that wants to use them but maintain their own internal state. The object’s internal state is kept private, meaning that it can only be accessed by the object’s own methods, not from other objects. Keeping an object’s internal state private, and generally making a clear division between its public interface and its private internal state, is called encapsulation.
This is a useful feature because it enables the programmer to change the internal implementation of an object without having to find and update all the code that uses it: it creates a kind of firewall between this object and the rest of the system.
For example, suppose students are allowed to study archery if they are in the second year or above. We could implement this just by exposing the student’s year property, and other code could examine that to decide whether the student can take the course:
That way, if we want to change the rules about studying archery, we only have to update the Student class, and all the code using it will still work.
In languages that don’t enforce access like this, programmers use naming conventions, such as starting the name with an underscore, to indicate that the property should be considered private.
OOP and JavaScript
In this article, we’ve described some of the basic features of class-based object-oriented programming as implemented in languages like Java and C++.
In the two previous articles, we looked at a couple of core JavaScript features: constructors and prototypes. These features certainly have some relation to some of the OOP concepts described above.
But it’s worth understanding the differences between these features and the «classical» OOP concepts described above. We’ll highlight a couple of them here.
First, in class-based OOP, classes and objects are two separate constructs, and objects are always created as instances of classes. Also, there is a distinction between the feature used to define a class (the class syntax itself) and the feature used to instantiate an object (a constructor). In JavaScript, we can and often do create objects without any separate class definition, either using a function or an object literal. This can make working with objects much more lightweight than it is in classical OOP.
Second, although a prototype chain looks like an inheritance hierarchy and behaves like it in some ways, it’s different in others. When a subclass is instantiated, a single object is created which combines properties defined in the subclass with properties defined further up the hierarchy. With prototyping, each level of the hierarchy is represented by a separate object, and they are linked together via the __proto__ property. The prototype chain’s behavior is less like inheritance and more like delegation. Delegation is a programming pattern where an object, when asked to perform a task, can perform the task itself or ask another object (its delegate) to perform the task on its behalf. In many ways, delegation is a more flexible way of combining objects than inheritance (for one thing, it’s possible to change or completely replace the delegate at run time).
That said, constructors and prototypes can be used to implement class-based OOP patterns in JavaScript. But using them directly to implement features like inheritance is tricky, so JavaScript provides extra features, layered on top of the prototype model, that map more directly to the concepts of class-based OOP. These extra features are the subject of the next article.
Summary
This article has described the basic features of class-based object oriented programming, and briefly looked at how JavaScript constructors and prototypes compare with these concepts.
In the next article, we’ll look at the features JavaScript provides to support class-based object-oriented programming.