C++ Programming: Programming language paradigms
A programming paradigm is a model of programming based on distinct concepts that shapes the way programmers design, organize and write programs. A multi-paradigm programming language allows programmers to choose a specific single approach or mix parts of different programming paradigms. C++ as a multi-paradigm programming language supports single or mixed approaches using Procedural or Object-oriented programming and mixing in utilizations of Generic and even Functional programming concepts.
Procedural programming can be defined as a subtype of imperative programming as a programming paradigm based upon the concept of procedure calls, in which statements are structured into procedures (also known as subroutines or functions). Procedure calls are modular and are bound by scope. A procedural program is composed of one or more modules. Each module is composed of one or more subprograms. Modules may consist of procedures, functions, subroutines or methods, depending on the programming language. Procedural programs may possibly have multiple levels or scopes, with subprograms defined inside other subprograms. Each scope can contain names which cannot be seen in outer scopes.
Procedural programming offers many benefits over simple sequential programming since procedural code:
- is easier to read and more maintainable
- is more flexible
- facilitates the practice of good program design
- allows modules to be used again in the form of code libraries.
Typing refers to how a computer language handles its variables, how they are differentiated by type. Variables are values that the program uses during execution. These values can change; they are variable, hence their name. Static typing usually results in compiled code that executes more quickly. When the compiler knows the exact types that are in use, it can produce machine code that does the right thing easier. In C++, variables need to be defined before they are used so that compilers know what type they are, and hence is statically typed. Languages that are not statically typed are called dynamically typed.
Static typing usually finds type errors more reliably at compile time, increasing the reliability of compiled programs. Simply put, it means that "A round peg won't fit in a square hole", so the compiler will report it when a type leads to ambiguity or incompatible usage. However, programmers disagree over how common type errors are and what proportion of bugs that are written would be caught by static typing. Static typing advocates believe programs are more reliable when they have been type checked, while dynamic typing advocates point to dynamic code that has proved reliable and to small bug databases. The value of static typing, then, presumably increases as the strength of the type system is increased.
A statically typed system constrains the use of powerful language constructs more than it constrains less powerful ones. This makes powerful constructs harder to use, and thus places the burden of choosing the "right tool for the problem" on the shoulders of the programmer, who might otherwise be inclined to use the most powerful tool available. Choosing overly powerful tools may cause additional performance, reliability or correctness problems, because there are theoretical limits on the properties that can be expected from powerful language constructs. For example, indiscriminate use of recursion or global variables may cause well-documented adverse effects.
Static typing allows construction of libraries which are less likely to be accidentally misused by their users. This can be used as an additional mechanism for communicating the intentions of the library developer.
Type checking is the process of verifying and enforcing the constraints of types, which can occur at either compile-time or run-time. Compile time checking, also called static type checking, is carried out by the compiler when a program is compiled. Run time checking, also called dynamic type checking, is carried out by the program as it is running. A programming language is said to be strongly typed if the type system ensures that conversions between types must be either valid or result in an error. A weakly typed language on the other hand makes no such guarantees and generally allows automatic conversions between types which may have no useful purpose. C++ falls somewhere in the middle, allowing a mix of automatic type conversion and programmer defined conversions, allowing for almost complete flexibility in interpreting one type as being of another type. Converting variables or expression of one type into another type is called type casting.
Object-oriented programming can be seen as an extension of procedural programming in which programs are made up of collections of individual units called objects that have a distinct purpose and function with limited or no dependencies on implementation. For example, a car is like an object; it gets you from point A to point B with no need to know what type of engine the car uses or how the engine works. Object-oriented languages usually provide a means of documenting what an object can and cannot do, like instructions for driving a car.
Objects and Classes
An object is composed of members and methods. The members (also called data members, characteristics, attributes, or properties) describe the object. The methods generally describe the actions associated with a particular object. Think of an object as a noun, its members as adjectives describing that noun, and its methods as the verbs that can be performed by or on that noun.
For example, a sports car is an object. Some of its members might be its height, weight, acceleration, and speed. An object's members just hold data about that object. Some of the methods of the sports car could be "drive", "park", "race", etc. The methods really do not mean much unless associated with the sports car, and the same goes for the members.
The "blueprint" that lets us build our sports car object is called a class. A class does not tell us how fast our sports car goes, or what color it is, but it does tell us that our sports car will have a member representing speed and color, and that they will be say, a number and a word, respectively. The class also lays out the methods for us, telling the car how to park and drive, but these methods can not take any action with just the blueprint - they need an object to have an effect.
Encapsulation, the principle of information hiding (from the user), is the process of hiding the data structures of the class and allowing changes in the data through a public interface where the incoming values are checked for validity, and so not only it permits the hiding of data in an object but also of behavior. This prevents clients of an interface from depending on those parts of the implementation that are likely to change in future, thereby allowing those changes to be made more easily, that is, without changes to clients. In modern programming languages, the principle of information hiding manifests itself in a number of ways, including encapsulation and polymorphism.
Inheritance describes a relationship between two (or more) types, or classes, of objects in which one is said to be a "subtype" or "child" of the other; as a result, the "child" object is said to inherit features of the parent, allowing for shared functionality. This lets programmers re-use or reduce code and simplifies the development and maintenance of software.
Inheritance is also commonly held to include subtyping, whereby one type of object is defined to be a more specialized version of another type (see Liskov substitution principle), though non sub-typing inheritance is also possible.
Inheritance is typically expressed by describing classes of objects arranged in an inheritance hierarchy (also referred to as inheritance chain), a tree-like structure created by their inheritance relationships.
For example, one might create a variable class "Mammal" with features such as eating, reproducing, etc.; then define a subtype "Cat" that inherits those features without having to explicitly program them, while adding new features like "chasing mice". This allows commonalities among different kinds of objects to be expressed once and reused multiple times.
In C++ we can then have classes that are related to other classes (a class can be defined by means of an older, pre-existing,
class ). This leads to a situation in which a new class has all the functionality of the older class, and additionally introduces its own specific functionality. Instead of composition, where a given class contains another class, we mean here derivation, where a given class is another class.
This OOP property will be explained further when we talk about Classes (and Structures) inheritance in the Classes Inheritance Section of the book.
If one wants to use more than one totally orthogonal hierarchy simultaneously, such as allowing "Cat" to inherit from "Cartoon character" and "Pet" as well as "Mammal" we are using multiple inheritance.
Multiple inheritance is the process by which one class can inherit the properties of two or more classes (variously known as its base classes, parent classes, ancestor classes, or super-classes).
This is shown in more detail in the C++ Classes Inheritance Section of the book.
Polymorphism allows a single name to be reused for several related but different purposes. The purpose of polymorphism is to allow one name to be used for a general class. Depending on the type of data, a specific instance of the general case is executed.
The concept of polymorphism is wider. Polymorphism exists every time we use two functions that have the same name, but differ in the implementation. They may also differ in their interface, e.g., by taking different arguments. In that case the choice of which function to make is via overload resolution, and is performed at compile time, so we refer to this as static polymorphism.
Dynamic polymorphism will be covered deeply in the Classes Section where we will address its use on redefining the method in the derived class.
Generic programming or polymorphism is a programming style that emphasizes techniques that allow one value to take on different types as long as certain contracts such as subtypes and signature are kept. In simpler terms generic programming is based in finding the most abstract representations of efficient algorithms. Templates popularized the notion of generics. Templates allow code to be written without consideration of the type with which it will eventually be used. Templates are defined in the Standard Template Library (STL), where generic programming was introduced into C++.
Free-form refers to how the programmer crafts the code. Basically, there are no rules on how you choose to write your program, save for the semantic rules of C++. Any C++ program should compile as long as it is legal C++.
The free-form nature of C++ is used (or abused, depending on your point of view) by some programmers in crafting obfuscated C++ (code that is purposefully written to be difficult to understand). In the right context this can also be seen as a show of craftsmanship (non functional but artful control over the language) but in general the use for obfuscation is seen only useful as a source security mechanism, ensuring that the source code is more intentionally difficult to analyze, replicate or use by third parties. With enough understanding about the compilers, source code can also be designed to preserve "water marks" in its compiled form that will permit tracing it to the original source.